<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mihai Serban | Software Engineer]]></title><description><![CDATA[Software engineer sharing insights on JavaScript, React, AWS, mobile development, and building products.]]></description><link>https://mihaiserban.dev</link><generator>GatsbyJS</generator><lastBuildDate>Sun, 13 Sep 2026 19:23:07 GMT</lastBuildDate><atom:link href="https://mihaiserban.dev/atom.xml" rel="self" type="application/rss+xml"/><item><title><![CDATA[An Example Semantic-Search Pipeline: Recall, Ranking, Freshness]]></title><description><![CDATA[A search for "how do I take money out of my account" should find "Withdrawal Methods". Keyword search can miss that connection when the…]]></description><link>https://mihaiserban.dev/blog/semantic-search-assembly-reconstruction/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/semantic-search-assembly-reconstruction/</guid><category><![CDATA[search]]></category><category><![CDATA[semantic-search]]></category><category><![CDATA[embeddings]]></category><category><![CDATA[architecture]]></category><pubDate>Wed, 15 Jul 2026 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A search for &lt;em&gt;&quot;how do I take money out of my account&quot;&lt;/em&gt; should find &lt;em&gt;&quot;Withdrawal Methods&quot;&lt;/em&gt;. Keyword search can miss that connection when the indexed text has no matching words. An embedding model may retrieve it because the texts express similar intent.&lt;/p&gt;
&lt;p&gt;This is an example knowledge-base search architecture. The candidate counts and ranking scores below are illustrative, not production measurements.&lt;/p&gt;
&lt;h2&gt;Lexical, semantic, and hybrid search&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Lexical search&lt;/strong&gt; such as BM25 is useful for exact strings including product codes, error messages, and proper nouns. &lt;strong&gt;Semantic search&lt;/strong&gt; can retrieve paraphrases, but rare names and identifiers may rank poorly. &lt;strong&gt;Hybrid search&lt;/strong&gt; combines candidates from both methods, for example with Reciprocal Rank Fusion, before reranking.&lt;/p&gt;
&lt;p&gt;Compare against a lexical baseline when users search for identifiers or error codes. A hybrid approach adds retrieval and tuning work, so measure whether it improves representative queries.&lt;/p&gt;
&lt;h2&gt;Two-stage retrieval&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Query → Query embedding → Vector search → 30 chunks → Reranker → Article results&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A bi-encoder embeds documents independently of queries. Store document vectors ahead of time, then embed a query and retrieve nearby vectors. A cross-encoder scores the resulting query-document pairs together. It adds work for every candidate, so the candidate set should be bounded.&lt;/p&gt;
&lt;p&gt;For the withdrawal query, retrieval might return chunks from &lt;em&gt;Withdrawal Methods&lt;/em&gt;, &lt;em&gt;Bank Transfer Limits&lt;/em&gt;, &lt;em&gt;ATM Cash Withdrawal&lt;/em&gt;, and &lt;em&gt;Account Closure&lt;/em&gt;. The reranker can reorder those chunks, after which the API groups them into articles. It cannot recover an article that retrieval did not supply.&lt;/p&gt;
&lt;h2&gt;Model choice includes language and input formatting&lt;/h2&gt;
&lt;p&gt;One possible embedding model is &lt;a href=&quot;https://huggingface.co/intfloat/multilingual-e5-small&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;intfloat/multilingual-e5-small&lt;/code&gt;&lt;/a&gt;, which produces 384-dimensional vectors. Its retrieval inputs should start with &lt;code class=&quot;language-text&quot;&gt;query: &lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;passage: &lt;/code&gt;, including non-English text:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;query: how do I take money out of my account
passage: Withdrawal Methods. You can withdraw funds by ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Follow the model&apos;s pooling and normalization guidance. L2 normalization makes the dot product equal cosine similarity; it does not calibrate relevance across queries. A score of 0.8 on one query need not carry the same meaning on another.&lt;/p&gt;
&lt;p&gt;For English, &lt;a href=&quot;https://huggingface.co/cross-encoder/ms-marco-TinyBERT-L2-v2&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cross-encoder/ms-marco-TinyBERT-L2-v2&lt;/code&gt;&lt;/a&gt; is a small reranker to evaluate. Its model card is tagged English and describes MS MARCO training. Pairing it with a multilingual embedding model does not establish multilingual ranking quality. Evaluate a multilingual knowledge base in each supported language.&lt;/p&gt;
&lt;p&gt;Benchmark inference with your text lengths, candidate count, batch size, and expected concurrency. Model names alone do not establish a latency target or whether a GPU is economical.&lt;/p&gt;
&lt;h2&gt;Vector storage with PostgreSQL and pgvector&lt;/h2&gt;
&lt;p&gt;If the application already uses PostgreSQL, pgvector can keep search data in the same database and transactions. Here is a starting schema for 384-dimensional vectors:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; EXTENSION &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; vector&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; embeddings &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  id BIGSERIAL &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  article_id &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  tenant &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  chunk_type &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  embedding VECTOR&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;384&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  created_at TIMESTAMPTZ &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NOW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; embeddings &lt;span class=&quot;token keyword&quot;&gt;USING&lt;/span&gt; hnsw &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;embedding vector_cosine_ops&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; embeddings &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tenant&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; article_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chunk_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;embedding &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&gt;&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;::vector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; similarity
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; embeddings
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; tenant &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; embedding &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&gt;&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;::vector
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ordering by the distance operator with a limit makes the query eligible to use the HNSW index, although PostgreSQL still chooses the plan. Check it with &lt;code class=&quot;language-text&quot;&gt;EXPLAIN ANALYZE&lt;/code&gt; on representative data.&lt;/p&gt;
&lt;p&gt;With approximate indexes, pgvector applies filters after scanning index candidates. A selective tenant or language filter can leave fewer than 30 results. Starting with pgvector 0.8.0, iterative index scans can search further up to configured limits. Depending on the workload, exact filtered search, partitioning, or partial indexes may fit better. Measure recall as well as query time. &lt;a href=&quot;https://github.com/pgvector/pgvector#filtering&quot;&gt;pgvector filtering documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A tenant column is one storage design, not an authorization boundary by itself. Derive the tenant from authenticated context and enforce article access rules in retrieval. Choose shared tables, row-level security, or stronger separation according to the application&apos;s isolation requirements.&lt;/p&gt;
&lt;h2&gt;Chunking changes what can be found&lt;/h2&gt;
&lt;p&gt;Titles, summaries, and sections are useful starting boundaries. Split oversized sections to fit the model&apos;s token limit while retaining enough context to identify the chunk. For text without useful boundaries, overlapping token windows are another option. Tune chunk size and overlap against representative questions.&lt;/p&gt;
&lt;p&gt;Each chunk points to its parent article. After reranking, one aggregation rule is to keep the best-scoring chunk per article. This avoids rewarding long articles simply for having many paragraphs, and means 30 retrieved chunks may produce fewer than 30 articles.&lt;/p&gt;
&lt;p&gt;Chunk-type weights are a heuristic to test. With illustrative nonnegative similarity scores, a title weight of 1.0 and paragraph weight of 0.7 gives:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Article A: Reset Multi-Factor Authentication
  title = 0.78; paragraph = 0.74
  weighted maximum = max(0.78 × 1.0, 0.74 × 0.7) = 0.78

Article B: Account Security Best Practices
  title = 0.70; paragraph = 0.82
  weighted maximum = max(0.70 × 1.0, 0.82 × 0.7) = 0.70&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Without weighting, B wins with 0.82 against A&apos;s 0.78. With these weights, A wins with 0.78 against B&apos;s 0.70. The example shows how the heuristic can change ordering; it does not establish that the changed order is better. A paragraph discount cannot guarantee that a title match wins. If applying weights to reranker outputs, account for their scale: multiplying a negative score by 0.7 increases it rather than penalizing it.&lt;/p&gt;
&lt;h2&gt;Ingestion and caching&lt;/h2&gt;
&lt;p&gt;For bulk imports or slow embedding work, an ingestion endpoint can validate and durably enqueue an update, then return &lt;code class=&quot;language-text&quot;&gt;202 Accepted&lt;/code&gt; with a tracking ID. A worker chunks, embeds, and stores it separately from search traffic.&lt;/p&gt;
&lt;p&gt;Prepare replacement embeddings before removing current ones. Replace an article&apos;s chunks in a transaction scoped by tenant and article ID, so readers see either the old set or the new set. Give updates versions and make retries idempotent; an older job finishing late must not overwrite newer content.&lt;/p&gt;
&lt;p&gt;A result cache avoids retrieval and reranking for repeated searches. Query-embedding caching is a separate option when the same query is reused with different filters. Stored document vectors do not eliminate query-embedding work.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;search:{tenant}:{generation}:{model_version}:{lang}:{access_hash}:{filter_hash}:{query_hash}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A result-cache key must include every input that changes the answer, including tenant, language, filters, access scope, and model or index version. Hash a canonical representation of the actual search inputs.&lt;/p&gt;
&lt;p&gt;Redis &lt;code class=&quot;language-text&quot;&gt;DEL&lt;/code&gt; accepts literal keys. &lt;code class=&quot;language-text&quot;&gt;DEL search:{tenant}:*&lt;/code&gt; does not expand the wildcard. One invalidation approach increments a tenant generation after an index update and uses that generation in subsequent cache keys. Another iterates with &lt;code class=&quot;language-text&quot;&gt;SCAN MATCH&lt;/code&gt; and deletes returned keys explicitly. Neither makes a database write and a Redis update atomic, so handle invalidation failures and avoid caching an in-flight result under a newer generation than the one it searched. &lt;a href=&quot;https://redis.io/docs/latest/commands/del/&quot;&gt;Redis DEL&lt;/a&gt;, &lt;a href=&quot;https://redis.io/docs/latest/commands/scan/&quot;&gt;SCAN&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Choose the TTL from the allowed staleness. Permission changes may need stronger handling than ordinary article edits; cached results must not bypass current access checks.&lt;/p&gt;
&lt;h2&gt;Measure before splitting services&lt;/h2&gt;
&lt;p&gt;Start with a deployment you can operate. Isolate ingestion from interactive searches when they compete for resources. Split embedding and reranking into independently scaled services when measurements show different capacity or release needs.&lt;/p&gt;
&lt;p&gt;Candidate count, text length, batching, and concurrency affect reranker cost. Record throughput and end-to-end p50, p95, and p99 latency under load. Set a budget for the full request, including network and orchestration overhead. Stage timings locate bottlenecks, but adding stage percentiles does not produce an end-to-end percentile.&lt;/p&gt;
&lt;h2&gt;Evaluate with knowledge-base questions&lt;/h2&gt;
&lt;p&gt;Build a labeled set of queries and relevant article IDs. Include paraphrases, identifiers, supported languages, access restrictions, and queries with no relevant answer. Keep a held-out set when tuning models, chunk sizes, or weights.&lt;/p&gt;
&lt;p&gt;Track retrieval recall at the candidate cutoff, final MRR or nDCG, end-to-end latency, and freshness after updates. These measurements overlap: changing chunking or retrieval can change both the candidate set and final ranking.&lt;/p&gt;
&lt;p&gt;When a query fails, inspect its trace. If the relevant article never entered the candidate set, check ingestion, chunking, filters, and retrieval. If it arrived but ranked poorly, inspect reranker scores and article aggregation first. Keep the failing query as a regression example.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Moving Agent Results into Files and Starting Synthesis with a Fresh Context]]></title><description><![CDATA[My coding agents previously kept planning, worker results, and synthesis in one orchestrator conversation. On a multi-file refactor, three…]]></description><link>https://mihaiserban.dev/blog/governance-worker-synthesis-assembly-reconstruction/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/governance-worker-synthesis-assembly-reconstruction/</guid><category><![CDATA[ai-agents]]></category><category><![CDATA[llm]]></category><category><![CDATA[multi-agent]]></category><category><![CDATA[context-window]]></category><category><![CDATA[research]]></category><pubDate>Wed, 15 Jul 2026 17:45:00 GMT</pubDate><content:encoded>&lt;p&gt;My coding agents previously kept planning, worker results, and synthesis in one orchestrator conversation. On a multi-file refactor, three worker results accumulated there before the final response. It was difficult to inspect the handoffs or give synthesis a focused input.&lt;/p&gt;
&lt;p&gt;That is a constraint of this workflow, not a verdict on every orchestrator design. A coordinator can still be useful for planning and recovery. I changed where detailed worker output lives and how synthesis starts.&lt;/p&gt;
&lt;h2&gt;The three-stage workflow&lt;/h2&gt;
&lt;p&gt;I use three stages backed by filesystem state: governance, a variable number of workers, and synthesis.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;User → Governance (plans, writes task specs to .agent-state/tasks/*.yaml)
         │
         │  spawns workers with: &quot;Read task spec. Write result to file. Confirm.&quot;
         ▼
    ┌──────────┐  ┌──────────┐  ┌──────────┐
    │ Worker 1 │  │ Worker 2 │  │ Worker N │   ← fresh contexts
    └────┬─────┘  └────┬─────┘  └────┬─────┘
         │             │             │
         ▼             ▼             ▼
    .agent-state/results/*.yaml
         │
         ▼
    Synthesis agent (fresh context, reads result files, produces final answer)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I configure governance to plan, write task specifications, and delegate rather than perform the assigned unit of work or synthesize the final response. Each worker receives a task-spec path and result path. Workers write structured results to YAML and return a short confirmation. When the fanout finishes, a fresh synthesis agent reads the result files and produces the final response. This separation is a workflow convention; it needs tool permissions if governance must be technically prevented from editing project files.&lt;/p&gt;
&lt;p&gt;An illustrative task specification:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .agent-state/tasks/analyze-auth.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; audit the authentication module for security issues
&lt;span class=&quot;token key atrule&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; do not modify the database schema
&lt;span class=&quot;token key atrule&quot;&gt;expected_output_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .agent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;state/results/analyze&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auth.yaml
&lt;span class=&quot;token key atrule&quot;&gt;context_files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;src/auth/middleware.ts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; src/auth/session.ts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;blocked_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;An illustrative result schema:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .agent-state/results/analyze-auth.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ok
&lt;span class=&quot;token key atrule&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example findings from a security review
&lt;span class=&quot;token key atrule&quot;&gt;findings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;severity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; critical
    &lt;span class=&quot;token key atrule&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example review finding
&lt;span class=&quot;token key atrule&quot;&gt;files_changed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;blockers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;verification_gaps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; could not verify third&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;party OAuth callback flow end&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The plugin I wrote, replacing the old orchestrator-minion watchdog, adds two capabilities beyond inactivity detection:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fanout completion tracking&lt;/strong&gt;: when all workers for a fanout are done, the plugin nudges governance to start synthesis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path-aware recovery&lt;/strong&gt;: when a worker stalls, the recovery prompt includes the task-spec and expected-result paths, so governance can inspect the state before deciding whether to wait, re-brief, or report a blocker.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;A watched worker may be inactive.
Task spec: .agent-state/tasks/analyze-auth.yaml
Expected result: .agent-state/results/analyze-auth.yaml
Inspect the child session, then decide: wait, re-brief, or report a blocker.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What the workflow changes&lt;/h2&gt;
&lt;p&gt;For a multi-file refactor, the intended differences are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Governance context&lt;/td&gt;
&lt;td&gt;Detailed worker results in the same conversation&lt;/td&gt;
&lt;td&gt;Worker confirmations and paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handoff format&lt;/td&gt;
&lt;td&gt;Natural-language summaries&lt;/td&gt;
&lt;td&gt;Structured YAML on disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synthesis context&lt;/td&gt;
&lt;td&gt;Same orchestrator conversation&lt;/td&gt;
&lt;td&gt;Fresh context reading result files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale worker recovery&lt;/td&gt;
&lt;td&gt;Session-only prompt&lt;/td&gt;
&lt;td&gt;Task-spec and result paths included&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The synthesis stage receives finished result files rather than governance discussion, worker chatter, and recovery prompts. That makes its input easier to inspect and reproduce. The post does not provide a controlled before-and-after quality study, so this is a workflow design benefit rather than a measured quality result.&lt;/p&gt;
&lt;h2&gt;Research that informed the design&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2505.21471&quot;&gt;ExtAgents&lt;/a&gt;&lt;/strong&gt; found benefits from distributing external knowledge across agents for its multi-hop question-answering and long-survey tasks. That supports testing parallel work when inputs are independent; it does not establish the same result for coding tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2511.02424&quot;&gt;ReAcTree&lt;/a&gt;&lt;/strong&gt; reports 61% goal success versus 31% for ReAct on WAH-NL with Qwen 2.5 72B. Its task-tree approach is useful when subgoals and dependencies are explicit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2606.30986&quot;&gt;The Organizational Behavior of Agentic AI&lt;/a&gt;&lt;/strong&gt; finds that human-imitation organization forms often underperform shared-state or adaptive forms under its tested interface conditions. I take that as a reason to make handoffs inspectable, not as a universal architecture rule.&lt;/p&gt;
&lt;h2&gt;What still needs work&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Elastic context (ACE).&lt;/strong&gt; &lt;a href=&quot;https://arxiv.org/abs/2606.31564&quot;&gt;ACE&lt;/a&gt; describes adaptive compression for message history. I have not implemented it. If governance itself grows during a large fanout, I would evaluate it against task-specific retrieval and summaries rather than assume one compression strategy is lossless.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dependency-aware fanout.&lt;/strong&gt; Right now, fanout assumes all workers are fully independent. If task B needs task A&apos;s output, governance must serialize them manually. ReAcTree&apos;s dynamic tree construction could automate this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structured handoff standardization.&lt;/strong&gt; The YAML schemas I use are project-specific. A small vocabulary such as &lt;code class=&quot;language-text&quot;&gt;status&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findings&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;files_changed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;blockers&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;verification_gaps&lt;/code&gt; is a useful starting point, but projects still need fields that match their tools and review process.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Training a Small Classifier to Route Coding Requests]]></title><description><![CDATA[I run an AI gateway between my coding agents and a pool of LLM providers. It handles health checks, failover, and model resolution, but it…]]></description><link>https://mihaiserban.dev/blog/neural-router-assembly-reconstruction/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/neural-router-assembly-reconstruction/</guid><category><![CDATA[llm]]></category><category><![CDATA[router]]></category><category><![CDATA[coding]]></category><category><![CDATA[ai-agents]]></category><pubDate>Wed, 15 Jul 2026 17:30:00 GMT</pubDate><content:encoded>&lt;p&gt;I run an AI gateway between my coding agents and a pool of LLM providers. It handles health checks, failover, and model resolution, but it does not inspect the content of a request before choosing a routing pool.&lt;/p&gt;
&lt;p&gt;I built a neural router that reads the first user message and classifies it as &lt;strong&gt;explore&lt;/strong&gt;, &lt;strong&gt;plan&lt;/strong&gt;, &lt;strong&gt;build&lt;/strong&gt;, or &lt;strong&gt;quick&lt;/strong&gt;. The task type selects a model pool and configured reasoning effort. The classifier is a 7,168-weight linear head on top of Qwen3-0.6B.&lt;/p&gt;
&lt;h2&gt;The architecture&lt;/h2&gt;
&lt;p&gt;The router is a sidecar service. The gateway calls it over HTTP, falls back to its configured default if the router is unavailable, and opens a circuit breaker after the first failure.&lt;/p&gt;
&lt;p&gt;The head is inspired by &lt;a href=&quot;https://arxiv.org/abs/2512.04695&quot;&gt;TRINITY&lt;/a&gt;, but it is a custom adaptation. It is one weight matrix, &lt;code class=&quot;language-text&quot;&gt;W ∈ R^{7×1024}&lt;/code&gt;, with no bias or activation: 7,168 weights. Seven logits split into four task logits and three reasoning-effort logits.&lt;/p&gt;
&lt;p&gt;TRINITY uses a different coordinator head with model-selection and role logits, and tunes additional backbone parameters. This router uses a fixed Qwen3-0.6B encoder and the 7-way head above. It mean-pools the final-layer token states into a 1024-dimensional, L2-normalized vector and multiplies it by the head.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;task_to_combo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;explore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; explorer     &lt;span class=&quot;token comment&quot;&gt;# exploration pool&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; planner         &lt;span class=&quot;token comment&quot;&gt;# planning pool&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder          &lt;span class=&quot;token comment&quot;&gt;# coding pool&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;quick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;fast     &lt;span class=&quot;token comment&quot;&gt;# short-request pool&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;task_to_reasoning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;explore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; low
  &lt;span class=&quot;token key atrule&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; high
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; high
  &lt;span class=&quot;token key atrule&quot;&gt;quick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; low&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Training data from my sessions&lt;/h2&gt;
&lt;p&gt;opencode stores sessions in a SQLite database. Each session has an &lt;code class=&quot;language-text&quot;&gt;agent&lt;/code&gt; field, which I used as a weak task-type label. I mapped the existing labels to four task types. The &lt;code class=&quot;language-text&quot;&gt;quick&lt;/code&gt; category was derived heuristically from build messages containing terms such as &quot;commit&quot;, &quot;git push&quot;, &quot;bash&quot;, or &quot;run&quot;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;opencode agent&lt;/th&gt;
&lt;th&gt;Task type&lt;/th&gt;
&lt;th&gt;Labeled rows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;explore, librarian&lt;/td&gt;
&lt;td&gt;explore&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plan&lt;/td&gt;
&lt;td&gt;plan&lt;/td&gt;
&lt;td&gt;192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;383&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build (commit, bash, git, etc.)&lt;/td&gt;
&lt;td&gt;quick&lt;/td&gt;
&lt;td&gt;181&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These category counts total 809. The source query can return multiple text parts per session, so a session-level evaluation needs an explicit selection rule and a split that keeps each session&apos;s examples together.&lt;/p&gt;
&lt;h2&gt;A local training run&lt;/h2&gt;
&lt;p&gt;In one local run, penultimate-token encoding with SGD reached 47.5% task accuracy. Its hidden-state cosine similarities were 0.3 to 0.5 both within and between these labels, so that representation did not separate the classes well in this dataset.&lt;/p&gt;
&lt;p&gt;I changed pooling and optimization for the next run. A penultimate hidden state can attend across the sequence; it is not a representation of only the last word. TRINITY&apos;s use of a penultimate state addresses a different coordinator design and input setting. The reported figures do not isolate the contribution of pooling from the optimizer change.&lt;/p&gt;
&lt;p&gt;The build class dominated, with 383 rows versus 53 explore rows. I used inverse-frequency class weights and Adam at &lt;code class=&quot;language-text&quot;&gt;lr=0.001&lt;/code&gt;; the epoch-200 training metrics were 78.6% task accuracy, 91.2% reasoning accuracy, and 0.73 loss.&lt;/p&gt;
&lt;p&gt;Treat these as training-run metrics. The figures here do not include a held-out split, seed, repeat count, or software versions. They also do not isolate pooling from the optimizer change. The reasoning labels come from the fixed task-to-reasoning mapping, so their accuracy mainly checks whether the head reproduced that derived label.&lt;/p&gt;
&lt;h2&gt;Inference on Apple Silicon&lt;/h2&gt;
&lt;p&gt;Qwen3-0.6B runs on MPS. In a recorded M3 Max run, latency was &lt;strong&gt;96ms warm and 624ms cold start&lt;/strong&gt;. The head weights file is 30KB. These are local observations, not a benchmark across machines or workloads.&lt;/p&gt;
&lt;p&gt;Task-head predictions from the recorded sample:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;Search the codebase for User model&quot; → explore (70.4%)
&quot;Write a function to parse markdown&quot; → build (68.5%)
&quot;Commit changes with fix message&quot; → quick (66.2%)
&quot;Design a real-time chat architecture&quot; → build (40.3%)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The raw reasoning output for the commit example was &lt;code class=&quot;language-text&quot;&gt;high (51.0%)&lt;/code&gt;, while the configuration maps &lt;code class=&quot;language-text&quot;&gt;quick&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;low&lt;/code&gt;. The post does not document how that conflict is handled, so the sample reports task predictions only.&lt;/p&gt;
&lt;p&gt;The plan/build boundary was the hardest in this label set. I currently map uncertain coding requests to build, but that is a routing-policy choice, not evidence that it is optimal.&lt;/p&gt;
&lt;h2&gt;What&apos;s next&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment-level optimization.&lt;/strong&gt; The router currently picks a model pool. A future version could score deployments within a pool using health, latency, price, and task success. TRINITY uses sep-CMA-ES to optimize its coordinator under an evaluation budget. A score such as &lt;code class=&quot;language-text&quot;&gt;quality - λ × cost&lt;/code&gt; would be my proposed objective and would need comparable-task measurements before provider selection could rely on it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-improving C-A-F loop.&lt;/strong&gt; &lt;a href=&quot;https://arxiv.org/abs/2606.22902&quot;&gt;Agent-as-a-Router&lt;/a&gt; describes a Context-Action-Feedback loop with routing, verification, and memory components. My gateway logs usage events to Postgres, including token counts, latency, served deployment, and cache hits. A verifier and a history of routing decisions would make an online-learning extension possible to evaluate; they would not by themselves establish an improvement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoiding routing collapse.&lt;/strong&gt; In their experiments, &lt;a href=&quot;https://arxiv.org/abs/2602.03478&quot;&gt;When Routing Collapses&lt;/a&gt; describes routers increasingly selecting expensive models as the cost budget rises and proposes ranking-based EquiRouter. I would evaluate a ranking approach when this router moves to deployment-level routing.&lt;/p&gt;
&lt;p&gt;This 7,168-weight head uses a local, weakly labeled dataset to select a task pool. It needs a documented split, baseline, repeated evaluation, and cost per successfully completed comparable task before it can support a claim about savings.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Runtime Skill Evals: An Assembly-Theoretic Reconstruction]]></title><description><![CDATA[We compared coding-agent outputs with and without a loaded skill to see which behaviors changed. Reading a skill can reveal unclear…]]></description><link>https://mihaiserban.dev/blog/runtime-skill-evals-as-assembly-theory/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/runtime-skill-evals-as-assembly-theory/</guid><category><![CDATA[skills]]></category><category><![CDATA[evaluation]]></category><category><![CDATA[assembly-theory]]></category><category><![CDATA[agent-skills]]></category><category><![CDATA[benchmarking]]></category><pubDate>Wed, 15 Jul 2026 17:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We compared coding-agent outputs with and without a loaded skill to see which behaviors changed.&lt;/p&gt;
&lt;p&gt;Reading a skill can reveal unclear instructions, but runtime tests show how the agent responds to them. For example, a reproduction step adds little on a task where the agent already reproduces the bug without prompting.&lt;/p&gt;
&lt;p&gt;You need a runtime test: run the same task with and without the skill, grade the outputs against assertions, and measure the delta. Anthropic&apos;s &lt;a href=&quot;https://github.com/anthropics/skills/tree/main/skills/skill-creator&quot;&gt;skill-creator&lt;/a&gt; does this via subagent spawning. We wanted the same thing, but using the &lt;a href=&quot;https://pi.dev&quot;&gt;pi coding agent&lt;/a&gt; as our harness.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mihaiserban/skills&quot;&gt;skill pack&lt;/a&gt; contains 29 skills for engineering workflows, design translation, git operations and research. Each skill is a &lt;code class=&quot;language-text&quot;&gt;SKILL.md&lt;/code&gt; file with frontmatter and instructions that an agent loads on-demand.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Method&lt;/h2&gt;
&lt;p&gt;The eval pipeline has three stages, all scriptable and CI-friendly:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;eval-runner    →  runs each eval with and without the skill (parallel)
eval-grader    →  grades outputs against assertions (batch LLM)
eval-aggregator →  produces benchmark + review artifacts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The baseline needs &lt;strong&gt;skill isolation&lt;/strong&gt;. In this harness, global skill directories are temporarily moved so the baseline cannot discover them. The other variant explicitly loads the tested skill and any declared dependencies.&lt;/p&gt;
&lt;p&gt;For skills with dependencies (an orchestrator that routes to sub-skills, for instance), the eval declares those dependencies and the runner loads them all. Composition is a real architectural concern, not an afterthought.&lt;/p&gt;
&lt;p&gt;Each eval carries 3–7 assertions that check specific, verifiable outcomes. The grader receives all assertions for one eval variant in a single model call and returns numbered PASS/FAIL verdicts. Those judgments still need review, especially when the score disagrees with the observed behavior.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Numbers&lt;/h2&gt;
&lt;p&gt;25 skills. 2 evals each. 100 total runs across 8 parallel workers.&lt;/p&gt;
&lt;p&gt;97 runs completed. Three timed out in the &quot;with skill&quot; variant during code generation. The timeouts need investigation alongside the completed-run scores.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;20 of 25 skills show positive delta.&lt;/strong&gt; 4 show no measurable difference. 1 shows a negative delta.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What the Deltas Tell You&lt;/h2&gt;
&lt;p&gt;The score differences suggest several places to inspect the outputs:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Largest improvements.&lt;/strong&gt; Governance, design and audit skills had some of the largest gains in assertion pass rate. They specify a plan → delegate → synthesize workflow, a wireframe before code, or an audit rubric. These results concern the tested tasks, not everything the model can do without those instructions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smaller improvements.&lt;/strong&gt; Some gains came from particular assertions, such as a git cleanup step or an output-format requirement. The individual outputs show which step the skill helped with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No measured difference.&lt;/strong&gt; The skill may add little on a given task, or the assertions may miss the behavior it changes. Equal scores alone do not distinguish these explanations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lower score with the skill.&lt;/strong&gt; The domain-modeling variant asked for more context and scored 67 percentage points lower. Review the prompt and response to decide whether the request was warranted. The score alone cannot tell you whether the skill or the evaluation needs changing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Reviewing the results&lt;/h2&gt;
&lt;p&gt;The recorded 100-run benchmark took about 15 minutes with 8 parallel workers. Its per-eval outputs help identify changed behavior and failures. With two evals per skill and an LLM grader, broader conclusions need more tasks, repeated runs and a review of the judgments.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mihaiserban/skills&quot;&gt;repository&lt;/a&gt; includes the skills, harness and evals needed to run the comparison.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Runtime Skill Evals With Pi: Measuring What Agent Skills Actually Change]]></title><description><![CDATA[How we built a parallel eval harness using the pi coding agent to benchmark 25 agent skills with and without skill context.]]></description><link>https://mihaiserban.dev/blog/runtime-skill-evals-with-pi/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/runtime-skill-evals-with-pi/</guid><category><![CDATA[skills]]></category><category><![CDATA[evaluation]]></category><category><![CDATA[pi]]></category><category><![CDATA[agent-skills]]></category><category><![CDATA[benchmarking]]></category><pubDate>Fri, 10 Jul 2026 16:30:00 GMT</pubDate><content:encoded>&lt;p&gt;We ran the same coding tasks with and without agent skills, then compared the outputs against a set of assertions. This post covers the harness and the results from 25 skills.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mihaiserban/skills&quot;&gt;repository&lt;/a&gt; contains 29 agent skills for engineering workflows, design translation, git operations and research. Each skill is a &lt;code class=&quot;language-text&quot;&gt;SKILL.md&lt;/code&gt; file with frontmatter and instructions that an agent loads on-demand.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Static review tells you a skill is well-written. It doesn&apos;t tell you whether the skill changes outcomes.&lt;/p&gt;
&lt;p&gt;A skill that says &quot;always reproduce the bug before fixing it&quot; is good advice. But if the model already does that by default, the skill adds tokens without changing behavior. You need a runtime test: run the same task with and without the skill, grade the outputs, and measure the delta.&lt;/p&gt;
&lt;p&gt;This is what &lt;a href=&quot;https://github.com/anthropics/skills/tree/main/skills/skill-creator&quot;&gt;Anthropic&apos;s skill-creator&lt;/a&gt; does: spawn subagents with and without the skill and compare results. We wanted the same thing, but using the &lt;a href=&quot;https://pi.dev&quot;&gt;pi coding agent&lt;/a&gt; as our harness.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Harness&lt;/h2&gt;
&lt;p&gt;The eval pipeline has three stages, all scriptable and CI-friendly:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;eval-runner.py     →  runs each eval with and without the skill (parallel)
eval-grade.py       →  grades outputs against assertions (batch LLM)
eval-aggregate.py   →  produces benchmark.json + HTML review&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The runner uses pi with full skill isolation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Without skill: zero skills loaded, no contamination&lt;/span&gt;
pi --no-skills --no-extensions &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; ~/.pi/agent/extensions/gateway &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
   --no-context-files --no-session &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;--model&lt;/span&gt; gateway/planner &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eval prompt&quot;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# With skill: only the tested skill, nothing else&lt;/span&gt;
pi --no-skills --no-extensions &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; ~/.pi/agent/extensions/gateway &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
   --no-context-files --no-session &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;--skill&lt;/span&gt; /path/to/SKILL.md &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;token parameter variable&quot;&gt;--model&lt;/span&gt; gateway/planner &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eval prompt&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;--no-skills&lt;/code&gt; flag prevents all skill discovery. Global skill directories (&lt;code class=&quot;language-text&quot;&gt;~/.agents/skills/&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;~/.pi/agent/skills/&lt;/code&gt;) are physically moved during eval runs to guarantee the baseline can&apos;t cheat. The &lt;code class=&quot;language-text&quot;&gt;--skill &amp;lt;path&gt;&lt;/code&gt; flag explicitly loads the tested skill alongside &lt;code class=&quot;language-text&quot;&gt;--no-skills&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For skills with dependencies (like the design orchestrator that routes to picker → apply → audit), the evals.json declares &lt;code class=&quot;language-text&quot;&gt;skill_deps&lt;/code&gt; and the runner passes multiple &lt;code class=&quot;language-text&quot;&gt;--skill&lt;/code&gt; flags.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Evals&lt;/h2&gt;
&lt;p&gt;25 skills, 2 evals each, 100 total runs across 8 parallel workers. Each eval has 3-7 assertions that check specific, verifiable outcomes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;skill_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;kill-dead-code&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;evals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;remove-unused-function&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;prompt&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Clean up this module. I think some functions are never called...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;assertions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;identifies-dead&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifies all four dead functions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;quality&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;keeps-live&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Keeps the two used exports&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;quality&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;warns-exports&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Warns unused exports might be public API&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;behavior&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Grading uses a batch LLM approach: all assertions for one eval variant go to a single &lt;code class=&quot;language-text&quot;&gt;gateway/coder&lt;/code&gt; call. The grader receives the model output in XML tags (to avoid code-fence collision bugs) and returns numbered PASS/FAIL verdicts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The Numbers&lt;/h2&gt;
&lt;p&gt;Of 100 runs, 97 completed and 3 timed out. All three timeouts were in the &lt;code class=&quot;language-text&quot;&gt;with_skill&lt;/code&gt; variant during code generation. The table shows assertion pass rates; delta is the percentage-point difference between the displayed rates.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;With Skill&lt;/th&gt;
&lt;th&gt;Without Skill&lt;/th&gt;
&lt;th&gt;Delta&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;governance-fanout&lt;/td&gt;
&lt;td&gt;89%&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;td&gt;+78 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;show-first&lt;/td&gt;
&lt;td&gt;85%&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;+70 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design-md-style-audit&lt;/td&gt;
&lt;td&gt;67%&lt;/td&gt;
&lt;td&gt;0%&lt;/td&gt;
&lt;td&gt;+67 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pr-from-diff&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;td&gt;+50 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design (orchestrator)&lt;/td&gt;
&lt;td&gt;89%&lt;/td&gt;
&lt;td&gt;44%&lt;/td&gt;
&lt;td&gt;+45 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;blog-post&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;td&gt;+40 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;context-budget&lt;/td&gt;
&lt;td&gt;88%&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;+38 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;systematic-debugging&lt;/td&gt;
&lt;td&gt;83%&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;+33 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;revert-surgical&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;78%&lt;/td&gt;
&lt;td&gt;+22 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;changelog-from-diff&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;+20 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;input-validation&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;+20 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design-md-style-apply&lt;/td&gt;
&lt;td&gt;83%&lt;/td&gt;
&lt;td&gt;67%&lt;/td&gt;
&lt;td&gt;+16 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design-taste-distiller&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;33%&lt;/td&gt;
&lt;td&gt;+17 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;research&lt;/td&gt;
&lt;td&gt;58%&lt;/td&gt;
&lt;td&gt;42%&lt;/td&gt;
&lt;td&gt;+16 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;kill-dead-code&lt;/td&gt;
&lt;td&gt;71%&lt;/td&gt;
&lt;td&gt;57%&lt;/td&gt;
&lt;td&gt;+14 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;decision-record&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;89%&lt;/td&gt;
&lt;td&gt;+11 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;adversarial-verify&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;+10 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sql-review&lt;/td&gt;
&lt;td&gt;70%&lt;/td&gt;
&lt;td&gt;60%&lt;/td&gt;
&lt;td&gt;+10 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;secret-scan&lt;/td&gt;
&lt;td&gt;36%&lt;/td&gt;
&lt;td&gt;27%&lt;/td&gt;
&lt;td&gt;+9 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clean-commits&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;91%&lt;/td&gt;
&lt;td&gt;+9 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design-md-style-picker&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;0 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bisect-regression&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;0 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;contract-test&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;td&gt;0 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rebase-safely&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td&gt;0 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;domain-modeling&lt;/td&gt;
&lt;td&gt;11%&lt;/td&gt;
&lt;td&gt;78%&lt;/td&gt;
&lt;td&gt;-67 pp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;20 of 25 skills show positive delta.&lt;/strong&gt; 4 show no measurable difference. 1 shows a negative delta.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What the Deltas Tell You&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Largest improvements.&lt;/strong&gt; The biggest gains came from skills specifying a workflow: plan → delegate → synthesize for &lt;code class=&quot;language-text&quot;&gt;governance-fanout&lt;/code&gt;, a wireframe before code for &lt;code class=&quot;language-text&quot;&gt;show-first&lt;/code&gt;, and an audit rubric for &lt;code class=&quot;language-text&quot;&gt;design-md-style-audit&lt;/code&gt;. The results show higher assertion pass rates on these tasks; they do not establish what the model could never do without a skill.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smaller improvements.&lt;/strong&gt; Other skills helped with particular assertions, such as a cleanup step in a git workflow or the output format required by &lt;code class=&quot;language-text&quot;&gt;changelog-from-diff&lt;/code&gt;. Inspect the individual outputs to see which behavior changed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No measured difference.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;contract-test&lt;/code&gt; scored 90% with and without its skill; &lt;code class=&quot;language-text&quot;&gt;rebase-safely&lt;/code&gt; scored 80% in both variants. Equal scores could mean the skill adds little on these tasks, or that the assertions miss the behavior it changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lower score with the skill.&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;domain-modeling&lt;/code&gt; scored 11% with the skill and 78% without it. The skill-loaded variant asked for more domain context. Review whether that request was warranted by the prompt before deciding whether to change the skill or the evaluation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Running the Benchmark&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Full pipeline: run → grade → aggregate, all skills, 8 parallel workers&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;bash&lt;/span&gt; scripts/start-evals.sh &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Or step by step&lt;/span&gt;
python3 scripts/eval-runner.py &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;
python3 scripts/eval-grade.py &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--parallel&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--model&lt;/span&gt; gateway/coder
python3 scripts/eval-aggregate.py &lt;span class=&quot;token parameter variable&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--output&lt;/span&gt; html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each skill gets &lt;code class=&quot;language-text&quot;&gt;eval-results/iteration-1/&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;benchmark.json&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;benchmark.md&lt;/code&gt;, and a &lt;code class=&quot;language-text&quot;&gt;review.html&lt;/code&gt; showing per-eval outputs and assertion pass/fail.&lt;/p&gt;
&lt;p&gt;Skills that reference other skills, such as the design orchestrator routing to picker → apply → audit, declare &lt;code class=&quot;language-text&quot;&gt;skill_deps&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;evals.json&lt;/code&gt;. The runner loads these dependencies with additional &lt;code class=&quot;language-text&quot;&gt;--skill&lt;/code&gt; flags.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Reviewing a run&lt;/h2&gt;
&lt;p&gt;The recorded 100-run benchmark took about 15 minutes with 8 parallel workers. Use the per-eval outputs to investigate score changes and timeouts. Two evals per skill and an LLM grader are a starting point for finding problems, not a general verdict on each skill.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/mihaiserban/skills&quot;&gt;repository&lt;/a&gt; includes the 29 skills, harness and evals. Run &lt;code class=&quot;language-text&quot;&gt;bash scripts/start-evals.sh --all&lt;/code&gt; to generate the results and review artifacts.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Training a Small Classifier to Route My Coding Requests]]></title><description><![CDATA[How I adapted a 7,168-weight linear head on top of Qwen3-0.6B to classify coding requests by task type and choose a routing pool.]]></description><link>https://mihaiserban.dev/blog/neural-llm-router-coding-assistant/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/neural-llm-router-coding-assistant/</guid><category><![CDATA[llm]]></category><category><![CDATA[router]]></category><category><![CDATA[coding]]></category><category><![CDATA[ai-agents]]></category><pubDate>Tue, 07 Jul 2026 05:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I run an AI gateway that sits between my coding agents and a pool of LLM providers. The gateway handles health checks, failover, and model resolution. But it had one blind spot: it never looked at the &lt;em&gt;content&lt;/em&gt; of what I was asking.&lt;/p&gt;
&lt;p&gt;Every request was routed based on whatever model the agent picked. If opencode chose &lt;code class=&quot;language-text&quot;&gt;coder&lt;/code&gt;, the gateway used that pool. That leaves the gateway unable to distinguish an exploration request such as &quot;search the codebase for User model references&quot; from a request that needs a stronger coding model.&lt;/p&gt;
&lt;p&gt;So I built a neural router that reads the &lt;em&gt;first user message&lt;/em&gt; and classifies it into one of four task types: &lt;strong&gt;explore&lt;/strong&gt;, &lt;strong&gt;plan&lt;/strong&gt;, &lt;strong&gt;build&lt;/strong&gt;, or &lt;strong&gt;quick&lt;/strong&gt;. The task type selects a model pool and a configured reasoning effort. The classifier is a 7,168-weight linear head on top of Qwen3-0.6B.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;The architecture&lt;/h2&gt;
&lt;p&gt;The router is a sidecar service. The gateway calls it over HTTP, falls back to the default model if it&apos;s unreachable, and opens a circuit breaker after the first failure.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;openCode/Codex → gateway :4100 → router sidecar :5560 → Qwen3-0.6B → head → {task,reasoning}
                                  │
                                  └─ (fallback) config.default_model&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The head is inspired by &lt;a href=&quot;https://arxiv.org/abs/2512.04695&quot;&gt;TRINITY&lt;/a&gt; (Xu et al., ICLR 2026), but it is a smaller custom adaptation. It is one weight matrix, &lt;code class=&quot;language-text&quot;&gt;W ∈ R^{7×1024}&lt;/code&gt;, with no bias or activation: 7,168 weights in total. Seven logits split into two softmax groups:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Dimensions&lt;/th&gt;
&lt;th&gt;Outputs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Task type&lt;/td&gt;
&lt;td&gt;4 logits&lt;/td&gt;
&lt;td&gt;explore, plan, build, quick&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reasoning effort&lt;/td&gt;
&lt;td&gt;3 logits&lt;/td&gt;
&lt;td&gt;low, medium, high&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;TRINITY uses a different coordinator head with model-selection and role logits, and tunes additional backbone parameters. This router instead uses a fixed Qwen3-0.6B encoder and the 7-way head above. At inference, it mean-pools the final-layer token states into a 1024-dimensional, L2-normalized vector and multiplies it by the head. The task output maps into the gateway&apos;s routing config:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;task_to_combo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;explore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; explorer     &lt;span class=&quot;token comment&quot;&gt;# exploration pool&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; planner         &lt;span class=&quot;token comment&quot;&gt;# planning pool&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder          &lt;span class=&quot;token comment&quot;&gt;# coding pool&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;quick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; coder&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;fast     &lt;span class=&quot;token comment&quot;&gt;# short-request pool&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;task_to_reasoning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;explore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; low
  &lt;span class=&quot;token key atrule&quot;&gt;plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; high
  &lt;span class=&quot;token key atrule&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; high
  &lt;span class=&quot;token key atrule&quot;&gt;quick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; low&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h2&gt;Training data came from my own sessions&lt;/h2&gt;
&lt;p&gt;I did not collect a separate dataset. opencode stores sessions in a SQLite database at &lt;code class=&quot;language-text&quot;&gt;~/.local/share/opencode/opencode.db&lt;/code&gt;. Each session has an &lt;code class=&quot;language-text&quot;&gt;agent&lt;/code&gt; field, which I used as a weak task-type label:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;model&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;session&lt;/span&gt; s
&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; message m &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;session_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; part p &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;agent &lt;span class=&quot;token operator&quot;&gt;IN&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;build&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;plan&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;explore&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;librarian&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;general&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; json_extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$.role&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;user&apos;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; json_extract&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;$.type&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;text&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;agent&lt;/code&gt; field was my label. I mapped it as follows:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;opencode agent&lt;/th&gt;
&lt;th&gt;Task type&lt;/th&gt;
&lt;th&gt;Labeled rows&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;explore, librarian&lt;/td&gt;
&lt;td&gt;explore&lt;/td&gt;
&lt;td&gt;53&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;plan&lt;/td&gt;
&lt;td&gt;plan&lt;/td&gt;
&lt;td&gt;192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;build&lt;/td&gt;
&lt;td&gt;383&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build (commit, bash, git, etc.)&lt;/td&gt;
&lt;td&gt;quick&lt;/td&gt;
&lt;td&gt;181&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;quick&lt;/code&gt; split is heuristic: build messages containing terms such as &quot;commit&quot;, &quot;git push&quot;, &quot;bash&quot;, or &quot;run&quot; were relabeled quick. These category counts total 809. The query can return multiple text parts per session, so a session-level evaluation needs an explicit selection rule and a split that keeps each session&apos;s examples together.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;A local training run&lt;/h2&gt;
&lt;p&gt;In one local run, penultimate-token encoding with SGD reached 47.5% task accuracy. Its hidden-state cosine similarities were 0.3–0.5 both within and between these labels, so that representation did not separate the classes well in this dataset.&lt;/p&gt;
&lt;p&gt;I changed pooling and optimization for the next run:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mean pooling instead of the penultimate token.&lt;/strong&gt; A penultimate hidden state can attend across the sequence; it is not a representation of only the last word. I switched to averaging token states for single-message classification. TRINITY&apos;s use of a penultimate state addresses a different coordinator design and input setting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adam with class-weighted loss.&lt;/strong&gt; The build class dominated (383 rows versus 53 explore rows). I used inverse-frequency class weights and Adam at &lt;code class=&quot;language-text&quot;&gt;lr=0.001&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Epoch&lt;/th&gt;
&lt;th&gt;Task accuracy&lt;/th&gt;
&lt;th&gt;Reasoning accuracy&lt;/th&gt;
&lt;th&gt;Loss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;67.3%&lt;/td&gt;
&lt;td&gt;86.4%&lt;/td&gt;
&lt;td&gt;1.47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;73.3%&lt;/td&gt;
&lt;td&gt;88.9%&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;td&gt;78.6%&lt;/td&gt;
&lt;td&gt;91.2%&lt;/td&gt;
&lt;td&gt;0.73&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Treat these as training-run metrics: the figures here do not include a held-out split, seed, repeat count or software versions. They also do not isolate the effect of pooling from the optimizer change. The reasoning labels come from the fixed task-to-reasoning mapping, so their accuracy mainly checks whether the head reproduced that derived label.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Inference on Apple Silicon&lt;/h2&gt;
&lt;p&gt;Qwen3-0.6B runs on MPS. This simplified FastAPI response shape shows the head outputs:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;python&quot;&gt;&lt;pre class=&quot;language-python&quot;&gt;&lt;code class=&quot;language-python&quot;&gt;&lt;span class=&quot;token decorator annotation punctuation&quot;&gt;@app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/route&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; RouteRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; RouteResponse&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    h &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; encoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;encode_mean_pool&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;transcript&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    task_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reasoning&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; debug &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; head&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;select&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;torch&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;as_tensor&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;h&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; RouteResponse&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        task_type&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;task_type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        reasoning_effort&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;reasoning&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The snippet returns the raw head prediction. It does not show which value the gateway ultimately applies when that prediction conflicts with the configuration.&lt;/p&gt;
&lt;p&gt;In the recorded M3 Max run, latency was &lt;strong&gt;96ms warm and 624ms cold start&lt;/strong&gt;. The head weights file is 30KB. These measurements are local observations, not a benchmark across machines or workloads.&lt;/p&gt;
&lt;p&gt;Task-head predictions from the recorded sample:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;Search the codebase for User model&quot; → explore (70.4%)
&quot;Write a function to parse markdown&quot;  → build   (68.5%)
&quot;Commit changes with fix message&quot;     → quick   (66.2%)
&quot;Design a real-time chat architecture&quot;→ build   (40.3%)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The raw reasoning output for the commit example was &lt;code class=&quot;language-text&quot;&gt;high (51.0%)&lt;/code&gt;, while the shown configuration maps &lt;code class=&quot;language-text&quot;&gt;quick&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;low&lt;/code&gt;. That disagreement needs a defined precedence rule at the gateway. The snippets here do not establish which value it ultimately applies, so the sample table reports only task predictions.&lt;/p&gt;
&lt;p&gt;The plan/build boundary was the hardest in this label set: &quot;design system architecture&quot; and &quot;write a function&quot; share structured, code-related vocabulary. I currently map uncertain coding requests to build, but that is a routing policy choice, not evidence that it is optimal.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;What&apos;s next&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deployment-level optimization.&lt;/strong&gt; Right now the router picks a combo bucket. A future version could score deployments within a bucket using health, latency, price, and task success. TRINITY uses sep-CMA-ES to optimize its coordinator under an evaluation budget. A score such as &lt;code class=&quot;language-text&quot;&gt;quality - λ × cost&lt;/code&gt; would be my own proposed objective and would need comparable-task measurements before I could use it to choose providers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-improving C-A-F loop.&lt;/strong&gt; &lt;a href=&quot;https://arxiv.org/abs/2606.22902&quot;&gt;Agent-as-a-Router&lt;/a&gt; describes a Context-Action-Feedback loop with routing, verification, and memory components. My gateway already logs usage events to Postgres, including token counts, latency, served deployment, and cache hits. A verifier and a history of routing decisions would make it possible to evaluate an online-learning extension; they would not by themselves establish that it improves routing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoiding routing collapse.&lt;/strong&gt; In their experiments, &lt;a href=&quot;https://arxiv.org/abs/2602.03478&quot;&gt;When Routing Collapses&lt;/a&gt; describes routers increasingly selecting expensive models as the cost budget rises and proposes ranking-based EquiRouter. I would evaluate a ranking approach when this router moves to deployment-level routing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Current scope&lt;/h2&gt;
&lt;p&gt;This 7,168-weight head uses a local, weakly labeled dataset to select a task pool for coding requests. The recorded warm latency was under 100ms on one M3 Max machine. It still needs a documented split, baseline, repeated evaluation, and cost per successfully completed comparable task before it can support a claim about accuracy beyond the training run or about savings. If the router is unavailable, the gateway falls back to its configured default.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Moving Agent Results into Files and Starting Synthesis with a Fresh Context]]></title><description><![CDATA[A file-based governance, worker, and synthesis workflow for coding tasks, plus research findings with their evaluation scope.]]></description><link>https://mihaiserban.dev/blog/replacing-orchestrator-agents-with-governance-worker-synthesis/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/replacing-orchestrator-agents-with-governance-worker-synthesis/</guid><category><![CDATA[ai-agents]]></category><category><![CDATA[llm]]></category><category><![CDATA[multi-agent]]></category><category><![CDATA[context-window]]></category><category><![CDATA[research]]></category><pubDate>Sun, 05 Jul 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My coding agents previously kept planning, worker results, and synthesis in one orchestrator conversation. On a multi-file refactor, three worker results accumulated in that conversation before the final response. It was difficult to inspect the handoffs or give synthesis a focused input.&lt;/p&gt;
&lt;p&gt;That is a constraint of this workflow, not a verdict on every orchestrator design. A coordinator can still be useful for planning and recovery. I changed where it keeps detailed worker output and how synthesis starts.&lt;/p&gt;
&lt;h2&gt;The three-stage workflow I implemented&lt;/h2&gt;
&lt;p&gt;I use three stages backed by filesystem state: governance, a variable number of workers, and synthesis.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;User → Governance (plans, writes task specs to .agent-state/tasks/*.yaml)
         │
         │  spawns workers with: &quot;Read task spec. Write result to file. Confirm.&quot;
         ▼
    ┌──────────┐  ┌──────────┐  ┌──────────┐
    │ Worker 1 │  │ Worker 2 │  │ Worker N │   ← fresh contexts
    └────┬─────┘  └────┬─────┘  └────┬─────┘
         │             │             │
         ▼             ▼             ▼   (writes to disk, returns short confirmation)
    .agent-state/results/*.yaml          ← shared filesystem state
         │
         ▼
    Synthesis agent (fresh context, reads result files, produces final answer)
         │
         ▼
    User ← clean, focused output&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I configure governance to plan, write task specifications, and delegate rather than perform the assigned unit of work or synthesize the final response. Each worker receives a task-spec path and result path. Workers write structured results to YAML and return a short confirmation. When the fanout finishes, a fresh synthesis agent reads the result files and produces the final response. This separation is a workflow convention; it needs tool permissions if governance must be technically prevented from editing project files.&lt;/p&gt;
&lt;p&gt;An illustrative task specification:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .agent-state/tasks/analyze-auth.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;goal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; audit the authentication module for security issues
&lt;span class=&quot;token key atrule&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; do not modify the database schema
&lt;span class=&quot;token key atrule&quot;&gt;expected_output_path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .agent&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;state/results/analyze&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;auth.yaml
&lt;span class=&quot;token key atrule&quot;&gt;context_files&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;src/auth/middleware.ts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; src/auth/session.ts&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;blocked_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null important&quot;&gt;null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;An illustrative result schema:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# .agent-state/results/analyze-auth.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ok
&lt;span class=&quot;token key atrule&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example findings from a security review
&lt;span class=&quot;token key atrule&quot;&gt;findings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;severity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; critical
    &lt;span class=&quot;token key atrule&quot;&gt;summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example review finding
&lt;span class=&quot;token key atrule&quot;&gt;files_changed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;blockers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;verification_gaps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; could not verify third&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;party OAuth callback flow end&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;end&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The plugin I wrote, replacing the old orchestrator-minion watchdog, adds two capabilities beyond inactivity detection:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fanout completion tracking&lt;/strong&gt;: when all workers for a fanout are done, the plugin nudges governance to start synthesis.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Path-aware recovery&lt;/strong&gt;: when a worker stalls, the recovery prompt includes the task-spec and expected-result paths, so governance can inspect the state before deciding whether to wait, re-brief, or report a blocker.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;A watched worker may be inactive.
Task spec: .agent-state/tasks/analyze-auth.yaml
Expected result: .agent-state/results/analyze-auth.yaml
Inspect the child session, then decide: wait, re-brief, or report a blocker.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What the workflow changes&lt;/h2&gt;
&lt;p&gt;For a multi-file refactor, the intended differences are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before (orchestrator-minion)&lt;/th&gt;
&lt;th&gt;After (governance-worker-synthesis)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Governance context&lt;/td&gt;
&lt;td&gt;Detailed worker results in the same conversation&lt;/td&gt;
&lt;td&gt;Worker confirmations and paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handoff format&lt;/td&gt;
&lt;td&gt;Natural language summaries&lt;/td&gt;
&lt;td&gt;Structured YAML on disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synthesis context&lt;/td&gt;
&lt;td&gt;Same orchestrator conversation&lt;/td&gt;
&lt;td&gt;Fresh context reading result files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stale worker recovery&lt;/td&gt;
&lt;td&gt;Session-only prompt&lt;/td&gt;
&lt;td&gt;Task-spec and result paths included&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The synthesis stage receives finished result files rather than governance discussion, worker chatter, and recovery prompts. That makes its input easier to inspect and reproduce. These workflow differences do not establish a quality improvement; that would require a controlled comparison on similar tasks.&lt;/p&gt;
&lt;h2&gt;Why I chose this structure&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2505.21471&quot;&gt;ExtAgents&lt;/a&gt;&lt;/strong&gt; found benefits from distributing external knowledge across agents for its multi-hop question-answering and long-survey tasks. That supports testing parallel work when inputs are independent; it does not establish the same result for coding tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2511.02424&quot;&gt;ReAcTree&lt;/a&gt;&lt;/strong&gt; reports 61% goal success versus 31% for ReAct on WAH-NL with Qwen 2.5 72B. Its task-tree approach is useful when subgoals and dependencies are explicit.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://arxiv.org/abs/2606.30986&quot;&gt;The Organizational Behavior of Agentic AI&lt;/a&gt;&lt;/strong&gt; finds that human-imitation organization forms often underperform shared-state or adaptive forms under its tested interface conditions. I take that as a reason to make handoffs inspectable, not as a universal architecture rule.&lt;/p&gt;
&lt;h2&gt;What still needs work&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Elastic context (ACE).&lt;/strong&gt; &lt;a href=&quot;https://arxiv.org/abs/2606.31564&quot;&gt;ACE&lt;/a&gt; describes adaptive compression for message history. I have not implemented it. If governance itself grows during a large fanout, I would evaluate it against task-specific retrieval and summaries rather than assume one compression strategy is lossless.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dependency-aware fanout.&lt;/strong&gt; Right now, fanout assumes all workers are fully independent. If task B needs task A&apos;s output, governance must serialize them manually. ReAcTree&apos;s dynamic tree construction could automate this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structured handoff standardization.&lt;/strong&gt; The YAML schemas I use are project-specific. A small vocabulary such as &lt;code class=&quot;language-text&quot;&gt;status&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;findings&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;files_changed&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;blockers&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;verification_gaps&lt;/code&gt; is a useful starting point, but projects still need fields that match their tools and review process.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building a Pattern Generator for My Front Gate]]></title><description><![CDATA[I built a browser tool to try perforated metal patterns for my front gate, with an SVG preview and PDF and DXF exports.]]></description><link>https://mihaiserban.dev/blog/building-a-design-pattern-generator-for-cnc-laser-cutting/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/building-a-design-pattern-generator-for-cnc-laser-cutting/</guid><category><![CDATA[react]]></category><category><![CDATA[gatsby]]></category><category><![CDATA[cnc]]></category><category><![CDATA[laser-cutting]]></category><category><![CDATA[svg]]></category><category><![CDATA[dxf]]></category><pubDate>Tue, 09 Jun 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wanted a metal front gate with a perforated pattern. When I called laser-cutting shops, they asked for a CAD file. I had an idea of the pattern, but no drawing to send them.&lt;/p&gt;
&lt;p&gt;I tried drawing patterns in Figma. Changing the spacing meant selecting, moving and copying shapes again. I wanted to adjust a few numbers and see what they would look like on a two-meter panel.&lt;/p&gt;
&lt;p&gt;I built the &lt;a href=&quot;/design-pattern-generator&quot;&gt;Design Pattern Generator&lt;/a&gt; to do that in the browser and export a DXF. It&apos;s a React page on this Gatsby site; generating the pattern and exporting files happen locally, without an account or an upload.&lt;/p&gt;
&lt;h2&gt;From settings to cutouts&lt;/h2&gt;
&lt;p&gt;The generator supports circles, squares and horizontal or vertical slots. I can change the sheet dimensions, margins, spacing and cutout size, then use a density gradient to vary the pattern across the panel. Squares and slots can have rounded corners.&lt;/p&gt;
&lt;p&gt;The engine returns shape descriptors, which the SVG preview renders. Here&apos;s a circle-pattern example with the margins supplied explicitly:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; shapes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generatePattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;marginTop&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;marginBottom&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;marginLeft&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;marginRight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;shapeType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;circle&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;shapeSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;spacing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;gradientType&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;topToBottom&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;opacity&lt;/code&gt; setting controls the target placement density, despite its name. It does not make the shapes translucent or specify the percentage of metal removed.&lt;/p&gt;
&lt;p&gt;For circles and squares, the engine builds a grid and decides which cells receive a cutout. It combines random placement with the 7/16, 3/16, 5/16 and 1/16 error-distribution weights from Floyd–Steinberg dithering. That spreads placement error across neighboring cells. The grid stays regular, but the occupied cells vary.&lt;/p&gt;
&lt;p&gt;Slots use a separate loop. It samples lengths between the configured minimum and maximum, leaves spacing between slots and uses the local density to decide whether to skip a position. This path does not use the grid&apos;s error diffusion.&lt;/p&gt;
&lt;p&gt;The directional density curve has an exponent of 1.8 and a floor of 10% of the density setting. That floor applies to the placement target, not a guaranteed number of cutouts. Generating the pattern again can change the result because the engine uses &lt;code class=&quot;language-text&quot;&gt;Math.random()&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Exporting a drawing&lt;/h2&gt;
&lt;p&gt;The preview uses SVG, and the download options are PDF and DXF.&lt;/p&gt;
&lt;p&gt;The PDF exporter uses &lt;code class=&quot;language-text&quot;&gt;svg2pdf.js&lt;/code&gt; and includes padding and a block of settings beside the drawing. Its page is therefore larger than the configured sheet. Print without “fit to page” scaling if you need to preserve the drawing scale.&lt;/p&gt;
&lt;p&gt;The DXF exporter writes circles as &lt;code class=&quot;language-text&quot;&gt;CIRCLE&lt;/code&gt; entities and slot and polygon outlines as closed &lt;code class=&quot;language-text&quot;&gt;LWPOLYLINE&lt;/code&gt; entities. The coordinates follow the tool&apos;s millimeter dimensions. Check the import units and geometry with the shop before cutting; a file format alone does not guarantee that every CAD/CAM setup interprets it as intended.&lt;/p&gt;
&lt;h2&gt;What the coverage number means&lt;/h2&gt;
&lt;p&gt;The coverage display is an estimate. The current calculation adds up circle areas and rectangular areas for squares and slots, then divides by the sheet&apos;s width times height. It does not subtract rounded corners or use the actual area of a nonrectangular sheet.&lt;/p&gt;
&lt;p&gt;For example, rounding a 20 mm square into a circle reduces its area from 400 mm² to about 314 mm², but the square calculation still counts 400 mm². The number is useful for comparing patterns with similar geometry; it is not an exact material-removal measurement.&lt;/p&gt;
&lt;p&gt;It also does not calculate panel strength or sound transmission. Material, thickness, remaining connections and mounting all matter to the finished panel. More cutout area means more open area, not proof that a gate is structurally suitable.&lt;/p&gt;
&lt;h2&gt;The gate pattern&lt;/h2&gt;
&lt;p&gt;I used vertical slots 30 mm wide and 100–400 mm long, with a density gradient and gaps between them. The DXF went to the laser shop. The roughly 35% cutout figure is an estimate, subject to the calculation limits above.&lt;/p&gt;
&lt;p&gt;Adjusting the parameters was much quicker than redrawing the pattern in Figma. I could compare several versions at the panel&apos;s dimensions before choosing a file to send.&lt;/p&gt;
&lt;p&gt;You can &lt;a href=&quot;/design-pattern-generator&quot;&gt;try the generator&lt;/a&gt; with your own dimensions and export the result as PDF or DXF.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A Two-Stage Search Pipeline for a Knowledge Base]]></title><description><![CDATA[An example search pipeline using embeddings, pgvector and reranking, with the chunking, filtering and evaluation decisions that affect its results.]]></description><link>https://mihaiserban.dev/blog/building-semantic-search-over-a-knowledge-base/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/building-semantic-search-over-a-knowledge-base/</guid><pubDate>Wed, 06 May 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A search for &lt;em&gt;&quot;how do I take money out of my account&quot;&lt;/em&gt; should find an article called &lt;em&gt;&quot;Withdrawal Methods&quot;&lt;/em&gt;. A keyword retriever can miss that connection when the indexed text lacks matching words or synonyms. An embedding model may retrieve it because the two texts express a similar intent.&lt;/p&gt;
&lt;p&gt;This is an example architecture for that kind of knowledge-base search. The candidate counts and ranking scores below are illustrative, not measurements from a deployed system.&lt;/p&gt;
&lt;h2&gt;Retrieve candidates, then rerank them&lt;/h2&gt;
&lt;p&gt;A bi-encoder embeds documents independently of queries. Store the document vectors ahead of time, then embed each query and retrieve nearby vectors. A cross-encoder can score the resulting query-document pairs together, using both texts to decide their relevance. That second step adds work for every candidate, so keep the candidate set bounded.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Query → Query embedding → Vector search → 30 chunks → Reranker → Article results&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For the withdrawal query, retrieval might return chunks from &lt;em&gt;Withdrawal Methods&lt;/em&gt;, &lt;em&gt;Bank Transfer Limits&lt;/em&gt;, &lt;em&gt;ATM Cash Withdrawal&lt;/em&gt; and &lt;em&gt;Account Closure&lt;/em&gt;. The reranker can reorder those chunks, after which the API groups them into article results. It cannot recover an article that retrieval never supplied.&lt;/p&gt;
&lt;p&gt;Two stages are an option to evaluate, not a requirement for every search feature. Compare against a lexical baseline such as BM25, especially when users paste error codes, product identifiers or API names. A hybrid retriever can combine lexical and vector candidates using a method such as Reciprocal Rank Fusion before reranking. Measure whether the extra retrieval and inference improve your own queries. &lt;a href=&quot;https://github.com/pgvector/pgvector#hybrid-search&quot;&gt;pgvector&apos;s hybrid-search examples&lt;/a&gt; show how these pieces can fit together.&lt;/p&gt;
&lt;h2&gt;Model choice includes language and input formatting&lt;/h2&gt;
&lt;p&gt;One possible embedding model is &lt;a href=&quot;https://huggingface.co/intfloat/multilingual-e5-small&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;intfloat/multilingual-e5-small&lt;/code&gt;&lt;/a&gt;, which produces 384-dimensional vectors. For retrieval, its inputs should start with &lt;code class=&quot;language-text&quot;&gt;query: &lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;passage: &lt;/code&gt;, including non-English text:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;query: how do I take money out of my account
passage: Withdrawal Methods. You can withdraw funds by ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Follow the model&apos;s pooling and normalization instructions. L2 normalization makes the dot product equal cosine similarity; it does not calibrate relevance across queries. A score of 0.8 on one query need not mean the same thing as 0.8 on another.&lt;/p&gt;
&lt;p&gt;For English, &lt;a href=&quot;https://huggingface.co/cross-encoder/ms-marco-TinyBERT-L2-v2&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;cross-encoder/ms-marco-TinyBERT-L2-v2&lt;/code&gt;&lt;/a&gt; is a small reranker to evaluate. Its model card is tagged English and describes MS MARCO training. Pairing it with a multilingual embedding model does not establish multilingual ranking quality. A multilingual knowledge base needs reranker evaluation in its supported languages too.&lt;/p&gt;
&lt;p&gt;Benchmark inference with your text lengths, candidate count, batch size and expected concurrency. A CPU deployment may be sufficient; these model names alone do not establish a latency target or whether a GPU is economical.&lt;/p&gt;
&lt;h2&gt;Store chunks in PostgreSQL&lt;/h2&gt;
&lt;p&gt;If the application already uses PostgreSQL, pgvector lets the search data share its database and transactions. Here is a starting schema for the 384-dimensional model:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; EXTENSION &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; vector&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; embeddings &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  id           BIGSERIAL &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  article_id   &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  tenant       &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt;     &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  chunk_type   &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt;         &lt;span class=&quot;token keyword&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  embedding    VECTOR&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;384&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  created_at   TIMESTAMPTZ &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NOW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; embeddings
  &lt;span class=&quot;token keyword&quot;&gt;USING&lt;/span&gt; hnsw &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;embedding vector_cosine_ops&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; embeddings &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tenant&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The following parameterized query uses &lt;code class=&quot;language-text&quot;&gt;$1&lt;/code&gt; for the query vector, &lt;code class=&quot;language-text&quot;&gt;$2&lt;/code&gt; for the tenant and &lt;code class=&quot;language-text&quot;&gt;$3&lt;/code&gt; for the language:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sql&quot;&gt;&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; article_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; chunk_type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
       &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;embedding &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&gt;&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;::vector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; similarity
&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; embeddings
&lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; tenant &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;AND&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;language&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;ORDER&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;BY&lt;/span&gt; embedding &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&gt;&lt;/span&gt; $&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;::vector
&lt;span class=&quot;token keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Ordering by the distance operator with a limit makes the query eligible to use the HNSW index; PostgreSQL still chooses the plan. Check it with &lt;code class=&quot;language-text&quot;&gt;EXPLAIN ANALYZE&lt;/code&gt; on representative data.&lt;/p&gt;
&lt;p&gt;There is a filtering catch: with approximate indexes, pgvector applies filters after scanning index candidates. A selective tenant or language filter can therefore leave fewer than 30 results. Starting with pgvector 0.8.0, iterative index scans can search further, up to their configured limits. Depending on the workload, exact search over filtered rows, partitioning or partial indexes may be appropriate. Measure recall as well as query time. &lt;a href=&quot;https://github.com/pgvector/pgvector#filtering&quot;&gt;pgvector filtering documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A tenant column is one storage design, not an authorization boundary by itself. Derive the tenant from authenticated context and enforce article access rules in retrieval. Choose shared tables, row-level security or stronger separation according to the application&apos;s isolation requirements.&lt;/p&gt;
&lt;h2&gt;Chunking changes what can be found&lt;/h2&gt;
&lt;p&gt;A long article can exceed the embedding model&apos;s input limit or contain several topics that fit poorly into one vector. Titles, summaries and sections are useful starting boundaries. Split oversized sections to fit the model&apos;s token limit, retaining enough context to identify what each chunk describes.&lt;/p&gt;
&lt;p&gt;For text without useful boundaries, overlapping token windows are another option. Tune chunk size and overlap against representative questions rather than assuming every document needs the same split.&lt;/p&gt;
&lt;p&gt;Each chunk points to its parent article. After reranking, one simple aggregation rule is to keep the best-scoring chunk per article. This avoids adding up many weak matches merely because an article is long. It also means 30 retrieved chunks may produce fewer than 30 distinct articles.&lt;/p&gt;
&lt;p&gt;Chunk-type weights are a further heuristic to test. For example, using illustrative nonnegative similarity scores, a title weight of 1.0 and paragraph weight of 0.7 gives:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Article A: Reset Multi-Factor Authentication
  title = 0.78; paragraph = 0.74
  weighted maximum = max(0.78 × 1.0, 0.74 × 0.7) = 0.78

Article B: Account Security Best Practices
  title = 0.70; paragraph = 0.82
  weighted maximum = max(0.70 × 1.0, 0.82 × 0.7) = 0.70&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Without weighting, B wins with 0.82 against A&apos;s 0.78. With these weights, A wins with 0.78 against B&apos;s 0.70. This constructed example shows how the heuristic can change the order; it does not establish that the new order is better for real queries. A paragraph discount cannot guarantee that every title match wins. If applying weights to reranker outputs, account for their scale: multiplying a negative score by 0.7 increases it rather than penalizing it.&lt;/p&gt;
&lt;h2&gt;Keep ingestion out of the search request&lt;/h2&gt;
&lt;p&gt;For bulk imports or slow embedding work, an ingestion endpoint can validate and durably enqueue an update, then return &lt;code class=&quot;language-text&quot;&gt;202 Accepted&lt;/code&gt; with a tracking ID. A worker chunks, embeds and stores it separately from search traffic.&lt;/p&gt;
&lt;p&gt;Prepare replacement embeddings before removing the current ones. Replace an article&apos;s chunks in a transaction, scoped by tenant and article ID, so readers see either the old set or the new set. Give updates versions and make retries idempotent; an older job finishing late must not overwrite newer content.&lt;/p&gt;
&lt;p&gt;A FIFO queue can preserve message order within a group, but that is only one part of processing order. Worker writes still need retry handling and version checks. Expose ingestion status so an accepted update is not mistaken for an already-searchable article. &lt;a href=&quot;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues-understanding-logic.html&quot;&gt;SQS FIFO ordering&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Cache results with an explicit freshness policy&lt;/h2&gt;
&lt;p&gt;A result cache avoids retrieval and reranking for repeated searches. Query-embedding caching is a separate option: it can save embedding work when the same query is reused with different filters. Stored document vectors do not eliminate query-embedding work.&lt;/p&gt;
&lt;p&gt;A result-cache key must include everything that changes the answer, including the tenant, language, filters, access scope and model/index version. For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;search:{tenant}:{generation}:{model_version}:{lang}:{access_hash}:{filter_hash}:{query_hash}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hash a canonical representation of the actual search inputs. Do not lowercase an identifier-sensitive query only for caching while sending its original case to retrieval.&lt;/p&gt;
&lt;p&gt;Redis &lt;code class=&quot;language-text&quot;&gt;DEL&lt;/code&gt; accepts literal keys. &lt;code class=&quot;language-text&quot;&gt;DEL search:{tenant}:*&lt;/code&gt; does not expand the wildcard. One invalidation approach is to increment a tenant generation after an index update and use that generation in subsequent cache keys. Old entries expire through a TTL. Another is to iterate with &lt;code class=&quot;language-text&quot;&gt;SCAN MATCH&lt;/code&gt; and delete the returned keys explicitly. Neither approach makes a database write and a Redis update atomic: handle invalidation failures, and avoid caching an in-flight result under a newer generation than the one it searched. &lt;a href=&quot;https://redis.io/docs/latest/commands/del/&quot;&gt;Redis DEL&lt;/a&gt;, &lt;a href=&quot;https://redis.io/docs/latest/commands/scan/&quot;&gt;SCAN&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Choose the TTL from the allowed staleness. Permission changes may need stronger handling than ordinary article edits; stale cached results must not bypass current access checks.&lt;/p&gt;
&lt;h2&gt;Measure before splitting services&lt;/h2&gt;
&lt;p&gt;Start with a deployment you can operate. Isolate ingestion work from interactive searches when it competes for the same resources. Split embedding and reranking into independently scaled services when measurements show that they need different capacity or release schedules.&lt;/p&gt;
&lt;p&gt;Candidate count, text length, batching and concurrency all affect reranker cost. Record throughput and end-to-end p50, p95 and p99 latency under load before choosing a service layout.&lt;/p&gt;
&lt;p&gt;Set a latency budget for the full request, including network and orchestration overhead. Measure stage durations to locate bottlenecks, but use the full request distribution to check the budget: adding stage percentiles does not produce an end-to-end percentile.&lt;/p&gt;
&lt;h2&gt;Evaluate with questions from the knowledge base&lt;/h2&gt;
&lt;p&gt;Build a labeled set of queries and relevant article IDs. Include paraphrases, identifiers, supported languages, access restrictions and queries with no relevant answer. Keep a held-out set when tuning models, chunk sizes or weights.&lt;/p&gt;
&lt;p&gt;Track retrieval recall at the candidate cutoff, final MRR or nDCG, end-to-end latency and freshness after updates. These measurements overlap: changing chunking or retrieval can change both the candidate set and its final ranking.&lt;/p&gt;
&lt;p&gt;When a query fails, inspect its trace. If the relevant article never entered the candidate set, check ingestion, chunking, filters and retrieval. If it arrived but ranked poorly, inspect reranker scores and article aggregation first. Keep the failing query as a regression example so the next change can be judged against it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cloning a Windows Drive from macOS with dd]]></title><description><![CDATA[How I copied a Windows drive on a dual-boot Hackintosh using a raw disk image, with checks before writing to the destination drive.]]></description><link>https://mihaiserban.dev/blog/cloning-windows-drive-from-mac-os-x-using-dd-disk-destroyer/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/cloning-windows-drive-from-mac-os-x-using-dd-disk-destroyer/</guid><pubDate>Thu, 07 Jan 2021 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I decided to upgrade my Windows drive on my dual-boot Hackintosh. Here is how I did it from macOS using &lt;code class=&quot;language-text&quot;&gt;dd&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Updated 2026-09-13:&lt;/strong&gt; &lt;code class=&quot;language-text&quot;&gt;dd&lt;/code&gt; makes a raw disk image, not an ISO installer image. The commands below can irreversibly overwrite a disk. Confirm every identifier with &lt;code class=&quot;language-text&quot;&gt;diskutil list&lt;/code&gt;, unmount the whole disk before copying, and replace the placeholders only after checking them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Open your terminal. Run &lt;code class=&quot;language-text&quot;&gt;diskutil list&lt;/code&gt; to get the disk identifier you want to migrate.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;diskutil list&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In my case the Windows drive is &lt;code class=&quot;language-text&quot;&gt;/dev/disk1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Unmount that disk, then clone it with &lt;code class=&quot;language-text&quot;&gt;dd&lt;/code&gt;. The &lt;code class=&quot;language-text&quot;&gt;r&lt;/code&gt; in &lt;code class=&quot;language-text&quot;&gt;/dev/rdisk1&lt;/code&gt; selects macOS&apos;s raw device, which can improve copying throughput. Save the output as a &lt;code class=&quot;language-text&quot;&gt;.img&lt;/code&gt; file on a different physical disk from the source, with enough free space. This can take hours.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;diskutil unmountDisk /dev/disk1
sudo dd if=/dev/rdisk1 of=/Users/mitzuuuu/Desktop/windows-drive.img bs=4m
shasum -a 256 /Users/mitzuuuu/Desktop/windows-drive.img&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install the empty drive and run &lt;code class=&quot;language-text&quot;&gt;diskutil list&lt;/code&gt; again to get its identifier. Do not assume it will be the same identifier as a previous drive. Its capacity must be at least as large as the source image.&lt;/p&gt;
&lt;p&gt;Unmount the destination disk, then write the image. This operation can also take hours.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;diskutil unmountDisk /dev/disk0
sudo dd if=/Users/mitzuuuu/Desktop/windows-drive.img of=/dev/rdisk0 bs=4m&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;of=&lt;/code&gt; is the destination. A wrong value overwrites that disk. The checksum records the image you created; keep it with the image so you can check the file before a later restore.&lt;/p&gt;
&lt;p&gt;After migration is done, take care of the unallocated space in Windows Disk Management. You can extend the Windows volume only when the unallocated space is adjacent to it and the partition layout permits it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to handle AWS SES bounces and complaints]]></title><description><![CDATA[If you’re thinking of implementing AWS Simple Email Service for your product, you might find out that you need a flow to handle email…]]></description><link>https://mihaiserban.dev/blog/how-to-handle-aws-ses-bounces-and-complaints/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/how-to-handle-aws-ses-bounces-and-complaints/</guid><category><![CDATA[AWS]]></category><pubDate>Thu, 01 Nov 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’re thinking of implementing AWS Simple Email Service for your product, you might find out that you need a flow to handle email bounces and complaints before AWS approves your service quota increase and take your SES account out of sandbox mode.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Updated 2026-09-13:&lt;/strong&gt; This guide reflects the 2018 SNS identity-notification workflow. Current SES can also publish events through configuration sets. Notification settings are scoped to the SES Region and sending identity, so confirm both before using the console steps below. Production access still requires a process for handling bounces and complaints.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This requirement assures SES maintains a high reputation for only delivering mail people want and thereby maintaining a high deliverability for legitimate mail.&lt;/p&gt;
&lt;p&gt;What exactly are email bounces and complaints?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bounce&lt;/strong&gt; email happens when an is returned to the sender because it cannot be delivered for some reason.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Complaints&lt;/strong&gt; are reports made by email recipients against emails they don’t want in their inbox. Mark as SPAM for example triggers such a report. Email Service Providers (ESPs), have what is called a “feedback loop” with all of the major Internet Service Provides (ISPs).&lt;/p&gt;
&lt;p&gt;In case your API receives a Bounce/Complaint you should take steps to make sure it doesn’t happen again. Easiest way is to not send emails to that user, unless he agrees to receive it.&lt;/p&gt;
&lt;h4&gt;Overview of the sending process&lt;/h4&gt;
&lt;p&gt;The following figure shows the process of sending an email via &lt;a href=&quot;http://aws.amazon.com/ses/&quot;&gt;AWS SES&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_pEz2kdAeiT6ljb32F6J9fw.png&quot; alt=&quot;AWS SES diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;If the sender request to SES succeds then it can expect one of the following outcomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;success&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;bounce&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;complaint&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Overview of handling of bounce/complaints&lt;/h4&gt;
&lt;p&gt;The following figure shows the process of handling bounce/complaints by using &lt;a href=&quot;https://aws.amazon.com/sns&quot;&gt;AWS SNS&lt;/a&gt; service.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_-eRHvZt-9R_R_7f6f0yCvw.png&quot; alt=&quot;AWS SES flow diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;Bounce and complaint notifications are available by email or through Amazon Simple Notification Service (Amazon SNS). By default, these notifications are sent to you via email by a feature called &lt;em&gt;email feedback forwarding&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;1. Setup AWS SNS topics for bounce and complaints&lt;/h4&gt;
&lt;p&gt;Create the following topics in &lt;a href=&quot;https://docs.aws.amazon.com/sns/latest/dg/sns-http-https-endpoint-as-subscriber.html#SendMessageToHttp.prepare&quot;&gt;AWS SNS&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ses-bounces-topic-prod&lt;/li&gt;
&lt;li&gt;ses-complaints-topic-prod&lt;/li&gt;
&lt;li&gt;ses-deliveries-topic-prod (optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_JZ8CVlRWquIv20SKnUIjHg.png&quot; alt=&quot;AWS SES create topic&quot;&gt;&lt;/p&gt;
&lt;p&gt;After creating each topic, you’ll receive a identity id is called &lt;strong&gt;ARN,&lt;/strong&gt; which we need in the next step of creating a SNS subscription.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_sWjh8Qxn-o5wyqvI5Ipe3Q.png&quot; alt=&quot;AWS SES topics&quot;&gt;&lt;/p&gt;
&lt;p&gt;Head to &lt;strong&gt;SNS Subscriptions&lt;/strong&gt; and create a SNS subscription for the bounce and complaint topics you’ve previously created.&lt;/p&gt;
&lt;p&gt;This is where we need to specify a Endpoint where we’ll receive notifications from each topic. Endpoint must be a &lt;strong&gt;POST&lt;/strong&gt; method on your backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1__Bnw9nIcRwC4LjH9hd3Mow.png&quot; alt=&quot;AWS SES create subscription&quot;&gt;&lt;/p&gt;
&lt;p&gt;Each Subscription needs to be confirmed, after creation they are in a &lt;strong&gt;PendingConfirmation&lt;/strong&gt; state.&lt;/p&gt;
&lt;p&gt;To confirm our subscription, we need to implement the endpoints in our backend, and call Request confirmations from the SNS dashboard.&lt;/p&gt;
&lt;p&gt;In the body received on our server we’ll find the SubscribeURL or Token which we can use to confirm.&lt;/p&gt;
&lt;p&gt;Call sns.confirmSubscription() with the Token or copy pasting SubscribeURL into SNS Dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_T_bSIOjMnaOaHs9PwEevnw.png&quot; alt=&quot;AWS SES confirm subscription url&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I’ve provided the code to subscribe and confirm each endpoint on&lt;/strong&gt; &lt;a href=&quot;https://gist.github.com/mihaiserban/8a03fd28e54cac8856dbdfebd95bd7b3&quot;&gt;&lt;strong&gt;Github&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP: Make sure your IAM User has access to SNS&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;2. Configure SES to publish notifications to each created SNS topic&lt;/h4&gt;
&lt;p&gt;In the 2018 SES console, go to &lt;strong&gt;Email Addresses&lt;/strong&gt;, select the sending identity, open &lt;strong&gt;Notifications&lt;/strong&gt;, and select the SNS topic for each notification type. In the current console, use the matching identity notification settings, or a configuration set when you need event publishing for a specific sending flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_k4fHq6CgGYUeXZjNmyXOLA.png&quot; alt=&quot;AWS SES notifications configuration&quot;&gt;&lt;/p&gt;
&lt;h4&gt;3. Testing using &lt;a href=&quot;https://aws.amazon.com/blogs/aws/mailbox-simulator-for-the-amazon-simple-email-service/&quot;&gt;AWS Mailbox Simulator&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The AWS mailbox simulator can be found in SES Managment Console and provides a way to test the way your implementation handles scenarios like bounces and complaints.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_Pydm3yb5aGuuRerVw6mxcQ.png&quot; alt=&quot;AWS SES test email&quot;&gt;&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:success@simulator.amazonses.com&quot;&gt;success@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will be treated as delivered successfully.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:bounce@simulator.amazonses.com&quot;&gt;bounce@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will be rejected with an SMTP 550 (“Unknown User”) response code. Amazon SES will send you a bounce notification by email or by SNS notification.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:ooto@simulator.amazonses.com&quot;&gt;ooto@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will be treated as delivered successfully.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:complaint@simulator.amazonses.com&quot;&gt;complaint@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; will simulate the case in which the recipient clicks &lt;strong&gt;Mark as Spam&lt;/strong&gt; within their email application and the ISP sends a complaint response to Amazon SES.&lt;/p&gt;
&lt;p&gt;Mail sent to &lt;strong&gt;&lt;a href=&quot;mailto:suppressionlist@simulator.amazonses.com&quot;&gt;suppressionlist@simulator.amazonses.com&lt;/a&gt;&lt;/strong&gt; simulates a hard bounce as though the recipient were on the Amazon SES global suppression list.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to fix broken images in React.]]></title><description><![CDATA[In one of my recent projects we encountered many images which were missing from our S3 bucket. When I see something like this it just makes me sick 😫.]]></description><link>https://mihaiserban.dev/blog/how-to-fix-broken-images-in-react/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/how-to-fix-broken-images-in-react/</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><pubDate>Wed, 24 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_xIlLqtM0dSTY3KZ03zckMg.png&quot; alt=&quot;Missing image example&quot;&gt;&lt;/p&gt;
&lt;p&gt;In one of my recent projects we encountered many images which were missing from our S3 bucket. When I see something like this it just makes me sick 😫.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;img&gt;&lt;/code&gt; provides us with two events, &lt;code class=&quot;language-text&quot;&gt;onLoad&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;onError&lt;/code&gt;. We can use these two to keep track of the status of the image.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;onError&lt;/code&gt; is called when our image has failed to load, and we can set &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; to our preferred fallback image.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;onLoad&lt;/code&gt; is called when our image loaded successfully, nothing for us to do here.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import React, { useEffect, useState } from &quot;react&quot;;

function Image({
  src = &quot;&quot;,
  placeholder = &quot;&quot;,
  disableContextMenu = false,
  onError,
  onContextMenu,
  ...other
}) {
  const [imageSrc, setImageSrc] = useState(src);

  useEffect(() =&gt; {
    setImageSrc(src);
  }, [src]);

  function handleImageError(event) {
    if (placeholder &amp;amp;&amp;amp; imageSrc !== placeholder) {
      setImageSrc(placeholder);
    }
    onError?.(event);
  }

  function handleContextMenu(event) {
    onContextMenu?.(event);
    if (disableContextMenu) {
      event.preventDefault();
    }
  }

  return (
    &amp;lt;img
      src={imageSrc}
      onError={handleImageError}
      {...other}
      onContextMenu={handleContextMenu}
    /&gt;
  );
}

export default Image;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;a href=&quot;https://gist.github.com/mihaiserban/751a84df361178db387e130d0c07693e&quot;&gt;original 2018 version is also available as a Gist&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Updated 2026-09-13:&lt;/strong&gt; The original example used &lt;code class=&quot;language-text&quot;&gt;componentWillReceiveProps&lt;/code&gt;, which is legacy React API. This version resets the image when &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; changes with an effect, and only attempts the fallback once.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Arrow Functions]]></title><description><![CDATA[Arrows are a function shorthand using the => syntax. Arrow functions allow you to preserve the lexical value of this.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-arrow-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-arrow-functions/</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_zxHFGY9JpcDDsB5vAcNMtg.png&quot; alt=&quot;ES6 Arrow functions&quot;&gt;&lt;/p&gt;
&lt;p&gt;Arrows are a function shorthand using the &lt;code class=&quot;language-text&quot;&gt;=&gt;&lt;/code&gt; syntax. Arrow functions allow you to preserve the lexical value of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Take the example below where we have a nested function, in which we would like to preserve the context of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; from its lexical scope:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Person(name) {  
    this.name = name;  
}  

Person.prototype.prefixName = function (arr) {  
    return arr.map(function (character) {  
        return this.name + character; // Cannot read property &apos;name&apos; of undefined  
    });  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using Arrow Functions, the lexical value of &lt;code class=&quot;language-text&quot;&gt;this&lt;/code&gt; isn&apos;t shadowed and we can re-write the above as shown:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Person(name) {  
    this.name = name;  
}  

Person.prototype.prefixName = function (arr) {  
    return arr.map(character =&gt; this.name + character);  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If an arrow is inside another function, it shares the &lt;code class=&quot;language-text&quot;&gt;arguments&lt;/code&gt; variable of its parent function. Example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// Lexical arguments  
function square() {  
  let example = () =&gt; {  
    let numbers = [];  
    for (let number of arguments) {  
      numbers.push(number * number);  
    }  

    return numbers;  
  };  

  return example();  
}  

square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JavaScript cheatsheet: Async/Await (ES2017)]]></title><description><![CDATA[Async/await was standardized in ES2017. Previous options for asynchronous code are callbacks and promises.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-async-await/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-async-await/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_U5MZoXlTOdyxlwf7XBqcHw.png&quot; alt=&quot;ES6 Async/Await&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Async/await was standardized in ES2017. Previous options for asynchronous code are callbacks and promises.&lt;/li&gt;
&lt;li&gt;Async/await is built on top of promises. It cannot be used with plain callbacks or node callbacks.&lt;/li&gt;
&lt;li&gt;Async/await makes asynchronous code look and behave a little more like synchronous code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In a regular script, &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt; may only be used in functions marked with the &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; keyword. ECMAScript modules can also use top-level &lt;code class=&quot;language-text&quot;&gt;await&lt;/code&gt;. It suspends execution in its context until the promise settles. If the awaited expression isn’t a promise, it is converted to one.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;async await&lt;/code&gt; allows us to perform the same thing we accomplished using Generators and Promises with less effort:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;async function getJSON(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }
  return response.json();
}

async function main() {
  try {
    const data = await getJSON(&apos;https://api.example.com/data&apos;);
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}  

main();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Under the hood, it performs similarly to &lt;a href=&quot;https://medium.com/@serbanmihai/javascript-es6-cheatsheet-generators-997cc977f7f1&quot;&gt;Generators&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Updated 2026-09-13:&lt;/strong&gt; The original callback wrapper depended on the now-deprecated &lt;code class=&quot;language-text&quot;&gt;request&lt;/code&gt; package, ignored errors, and called &lt;code class=&quot;language-text&quot;&gt;getJSON&lt;/code&gt; without a URL. The example now uses &lt;code class=&quot;language-text&quot;&gt;fetch&lt;/code&gt;, available in modern browsers and Node.js 18+.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Classes]]></title><description><![CDATA[Prior to ES6, we implemented Classes by creating a constructor function and  adding properties by extending the prototype]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-classes/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-classes/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_zdv7_BKFCrpmGwfPgu6j3A.png&quot; alt=&quot;ES6 Classes&quot;&gt;&lt;/p&gt;
&lt;p&gt;Prior to ES6, we implemented Classes by creating a constructor function and adding properties by extending the prototype:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Person(name, age, gender) {  
    this.name   = name;  
    this.age    = age;  
    this.gender = gender;  
}  

Person.prototype.incrementAge = function () {  
    return this.age += 1;  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And created extended classes by the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function Personal(name, age, gender, occupation, hobby) {  
    Person.call(this, name, age, gender);  
    this.occupation = occupation;  
    this.hobby = hobby;  
}  

Personal.prototype = Object.create(Person.prototype);  
Personal.prototype.constructor = Personal;  
Personal.prototype.incrementAge = function () {  
    Person.prototype.incrementAge.call(this);  
    this.age += 20;  
    console.log(this.age);  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ES6 classes are a simple sugar over the prototype-based OO pattern. Classes support prototype-based inheritance, super calls, instance and static methods and constructors:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Person {  
    constructor(name, age, gender) {  
        this.name   = name;  
        this.age    = age;  
        this.gender = gender;  
    }  

    incrementAge() {  
      this.age += 1;  
    }  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And extend them using the &lt;code class=&quot;language-text&quot;&gt;extends&lt;/code&gt; keyword:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Personal extends Person {  
    constructor(name, age, gender, occupation, hobby) {  
        super(name, age, gender);  
        this.occupation = occupation;  
        this.hobby = hobby;  
    }  

    incrementAge() {  
        super.incrementAge();  
        this.age += 20;  
        console.log(this.age);  
    }  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Destructuring]]></title><description><![CDATA[Destructuring is a convenient way of extracting multiple values from data stored in (possibly nested) objects and arrays.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-destructuring/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-destructuring/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_YujTHdJ1Hx9AWIe0CalxPg.png&quot; alt=&quot;ES6 Destructuring&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Destructuring&lt;/h3&gt;
&lt;p&gt;Destructuring is a convenient way of extracting multiple values from data stored in (possibly nested) objects and Arrays.&lt;/p&gt;
&lt;h3&gt;Array&lt;/h3&gt;
&lt;p&gt;Destructuring assignment allows you to assign the properties of an array using syntax that looks similar to array literals.&lt;/p&gt;
&lt;p&gt;Old way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var first = someArray[0];  
var second = someArray[1];  
var third = someArray[2];&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;New way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let [first, second, third] = someArray;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you want to declare your variables at the same time, you can add a &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; in front of the assignment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var [ variable1, variable2, ..., variableN ] = array;  
let [ variable1, variable2, ..., variableN ] = array;  
const [ variable1, variable2, ..., variableN ] = array;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can even skip a few variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let [,,third] = [&quot;foo&quot;, &quot;bar&quot;, &quot;baz&quot;];  
console.log(third); // &quot;baz&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There’s also no need to match the full array:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let array = [1, 2, 3, 4];  
let [a, b, c] = array;

console.log(a, b, c) // -------- 1  2  3&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can capture all trailing items in an array with a “rest” pattern:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const array = [1, 2, 3, 4];  
const [head, ...tail] = array;  
console.log(head); // 1  
console.log(tail); // [2, 3, 4]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Rest parameter must be applied as the last element, otherwise you’ll get a &lt;code class=&quot;language-text&quot;&gt;SyntaxError&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let array = [1, 2, 3, 4];  
let [...head, d] = array;  
// Uncaught SyntaxError: Unexpected token...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Object&lt;/h3&gt;
&lt;p&gt;Old way of destructuring an object:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var person = { first_name: &apos;Joe&apos;, last_name: &apos;Appleseed&apos; };  
var first_name = person.first_name; // &apos;Joe&apos;  
var last_name = person.last_name; // &apos;Appleseed&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;New way of destructuring an object:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let person = { first_name: &apos;Joe&apos;, last_name: &apos;Appleseed&apos; };  
let {first_name, last_name} = person;

console.log(first_name); // &apos;Joe&apos;  
console.log(last_name); // &apos;Appleseed&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When you destructure on properties that are not defined, you get undefined:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let { missing } = {};  
console.log(missing); // undefined&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also destructure in a for-of loop:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const arr = [&apos;a&apos;, &apos;b&apos;];  
for (const [index, element] of arr.entries()) {  
    console.log(index, element);  
}  
// Output:  
// 0 a  
// 1 b&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Object rest properties were standardized in ES2018. If you support older runtimes, transpile this syntax.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let object = {  
  a: &apos;A&apos;,  
  b: &apos;B&apos;,  
  c: &apos;C&apos;,  
  d: &apos;D&apos;,  
}

const { a, b, ...other } = object;
console.log(other); // {c: &apos;C&apos;, d: &apos;D&apos;}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Generators]]></title><description><![CDATA[A generator is a function which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-generators/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-generators/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_rwj0mwY2iJ391EP3EVb0PA.png&quot; alt=&quot;ES6 generators&quot;&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;generator&lt;/code&gt; is a function which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.&lt;/p&gt;
&lt;p&gt;Generators in JavaScript are a very powerful tool for asynchronous programming as they mitigate the problems with callbacks, such as &lt;code class=&quot;language-text&quot;&gt;Callback Hell&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Inversion of Control&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This pattern is what &lt;code class=&quot;language-text&quot;&gt;async&lt;/code&gt; functions are built on top of.&lt;/p&gt;
&lt;p&gt;For creating a generator function, we use &lt;code class=&quot;language-text&quot;&gt;function *&lt;/code&gt; syntax instead of just &lt;code class=&quot;language-text&quot;&gt;function&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead. When the iterator’s &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt; method is called, the generator function&apos;s body is executed until the first &lt;code class=&quot;language-text&quot;&gt;yield&lt;/code&gt; expression, which specifies the value to be returned from the iterator or, with &lt;code class=&quot;language-text&quot;&gt;yield*&lt;/code&gt;, delegates to another generator function.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt; method returns an object with a value property containing the yielded value and a done property which indicates whether the generator has yielded its last value as a boolean. Calling the &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt; method with an argument will resume the generator function execution, replacing the yield expression where execution was paused with the argument from &lt;code class=&quot;language-text&quot;&gt;next()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple example:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function* generator(i) {  
  yield i;  
  yield i + 10;  
}  

var gen = generator(10);  

console.log(gen.next().value);// expected output: 10  
console.log(gen.next().value); // expected output: 20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Example with yield*:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function* anotherGenerator(i) {  
  yield i + 1;  
  yield i + 2;  
  yield i + 3;  
}  

function* generator(i) {  
  yield i;  
  yield* anotherGenerator(i);  
  yield i + 10;  
}  

var gen = generator(10);  

console.log(gen.next().value); // 10  
console.log(gen.next().value); // 11  
console.log(gen.next().value); // 12  
console.log(gen.next().value); // 13  
console.log(gen.next().value); // 20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Infinite Data generator example:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function * naturalNumbers() {  
  let num = 1;  
  while (true) {  
    yield num;  
    num = num + 1  
  }  
}  
const numbers = naturalNumbers();

console.log(numbers.next().value) // 1
console.log(numbers.next().value) // 2 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Getter and setter functions]]></title><description><![CDATA[Let's take a look at getter and setter functions within ES6 classes. ]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-getter-and-setter-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-getter-and-setter-functions/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_rt1b55HKcfIWZQf-olDhbQ.png&quot; alt=&quot;ES6 getter and setter functions&quot;&gt;&lt;/p&gt;
&lt;p&gt;ES6 has started supporting getter and setter functions within classes. Using the following example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;class Person {  
    constructor(name) {  
        this._name = name;  
    }  

    get name() {  
      if(this._name) {  
        return this._name.toUpperCase();    
      } else {  
        return undefined;  
      }    
    }  

    set name(newName) {  
      if (newName == this._name) {  
        console.log(&apos;I already have this name.&apos;);  
      } else if (newName) {  
        this._name = newName;  
      } else {  
        return false;  
      }  
    }  
}  

let person = new Person(&quot;John Doe&quot;);  

// uses the get method in the background  
if (person.name) {  
  console.log(person.name);  // JOHN DOE
}  

// uses the setter in the background  
person.name = &quot;Jane Doe&quot;;  
console.log(person.name);  // JANE DOE&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Helpful array functions]]></title><description><![CDATA[Dive into new array functions introduced in ES6]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-array-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-array-functions/</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1__1bFnrLKJfM9oDP_twk8Pg.png&quot; alt=&quot;ES6 array functions&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**from**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const inventory = [  
    {name: &apos;mars&apos;, quantity: 2},  
    {name: &apos;snickers&apos;, quantity: 3}  
];  
console.log(Array.from(inventory, item =&gt; item.quantity + 2)); // [4, 5]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**of**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Array.of(&quot;Twinkle&quot;, &quot;Little&quot;, &quot;Star&quot;); // returns [&quot;Twinkle&quot;, &quot;Little&quot;, &quot;Star&quot;]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**find**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const inventory = [  
    {name: &apos;mars&apos;, quantity: 2},  
    {name: &apos;snickers&apos;, quantity: 3}  
];  
console.log(inventory.find(item =&gt; item.name === &apos;mars&apos;)); // {name: &apos;mars&apos;, quantity: 2}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**findIndex**&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const inventory = [  
    {name: &apos;mars&apos;, quantity: 2},  
    {name: &apos;snickers&apos;, quantity: 3}  
];  
console.log(inventory.findIndex(item =&gt; item.name === &apos;mars&apos;)); // 0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;**fill**&lt;/code&gt; method takes up to three arguments value, start and end. The start and end arguments are optional with default values of 0 and the length of the this object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3].fill(1); // [1, 1, 1]  
[1, 2, 3].fill(4, 1, 2); // [1, 4, 3]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Helpful string functions]]></title><description><![CDATA[Quick look at some helpful string functions introduced in ES6]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-string-functions/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-helpful-string-functions/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_1FqOzjfkPr33etNp8oqYew.png&quot; alt=&quot;ES6 string functions&quot;&gt;&lt;/p&gt;
&lt;h3&gt;.includes( )&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var string = &apos;string&apos;;  
var substring = &apos;str&apos;;  

console.log(string.indexOf(substring) &gt; -1);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Instead of checking for a return value &lt;code class=&quot;language-text&quot;&gt;&gt; -1&lt;/code&gt; to denote string containment, we can simply use &lt;code class=&quot;language-text&quot;&gt;.includes()&lt;/code&gt; which will return a boolean:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const string = &apos;string&apos;;  
const substring = &apos;str&apos;;  

console.log(string.includes(substring)); // true&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;.repeat( )&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function repeat(string, count) {  
    var strings = [];  
    while(strings.length &amp;lt; count) {  
        strings.push(string);  
    }  
    return strings.join(&apos;&apos;);  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In ES6, we now have access to a nicer implementation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// String.repeat(numberOfRepetitions)  
&apos;str&apos;.repeat(3); // &apos;strstrstr&apos;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Map & WeakMap]]></title><description><![CDATA[Quick look into new Map & WeakMap introduced in ES6]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-map-weakmap/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-map-weakmap/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_nK_beIXw-lIf1wTpd2kocg.png&quot; alt=&quot;ES6 Map snippet&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Map&lt;/h3&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; is a data structure allows to associate data to a key.&lt;/p&gt;
&lt;p&gt;Before it’s intruduction in ES6, people generally used objects as maps, by associating some object or value to a specific key value:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const person = {}  
person.name = &apos;John&apos;  
person.age = 18  

console.log(person.name) //John  
console.log(person.age) //18&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const person = new Map()  

person.set(&apos;name&apos;, &apos;John&apos;)  
person.set(&apos;age&apos;, 18)  

const name = person.get(&apos;name&apos;)  
const age = person.get(&apos;age&apos;)  

console.log(name) //John  
console.log(age) //18&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; also provide us with methods to help us manage the data.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;delete()&lt;/code&gt; method - deletes an item from a map by key:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;person.delete(&apos;name&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;clear()&lt;/code&gt; method - delete all items from a map:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;person.clear()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;has()&lt;/code&gt; method - check if a map contains an item by key:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const hasName = person.has(&apos;name&apos;)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;size()&lt;/code&gt; method - check the number of items in a map:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const size = person.size&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can also use a couple of methods to iterate:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;entries()&lt;/code&gt; returns all entries.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;keys()&lt;/code&gt; returns all keys.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;values()&lt;/code&gt; returns all values.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;WeakMap&lt;/h3&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; is a special kind of map.&lt;/p&gt;
&lt;p&gt;In a &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;, items are never garbage collected. A &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; instead lets all its items be freely garbage collected. Every key of a &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; is an object. When the reference to this object is lost, the value can be garbage collected.&lt;/p&gt;
&lt;p&gt;Main differences between &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you cannot iterate over the keys or values (or key-values) of a WeakMap&lt;/li&gt;
&lt;li&gt;you cannot clear all items from a WeakMap&lt;/li&gt;
&lt;li&gt;you cannot check its size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A WeakMap exposes those methods, which are equivalent to the Map ones:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;get(k)  
set(k, v)  
has(k)  
delete(k)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The use cases of a &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; are less evident than the ones of a &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;, and you might never find the need for them, but essentially it can be used to build a memory-sensitive cache that is not going to interfere with garbage collection, or for careful encapsualtion and information hiding.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Modules]]></title><description><![CDATA[Prior to ES6, we used libraries such as Browserify to create modules on the client-side, and require in Node.js. ES modules provide a standard import/export syntax.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-modules/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-modules/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_AVuC2PTg3VOE6EEZTfnydw.png&quot; alt=&quot;ES6 modules&quot;&gt;&lt;/p&gt;
&lt;p&gt;Prior to ES6, we used libraries such as &lt;a href=&quot;http://browserify.org/&quot;&gt;Browserify&lt;/a&gt; to create modules on the client-side, and &lt;a href=&quot;https://nodejs.org/api/modules.html#modules_module_require_id&quot;&gt;require&lt;/a&gt; in &lt;strong&gt;Node.js&lt;/strong&gt;. ES modules provide a separate, standard module system built around &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;export&lt;/code&gt;; CommonJS and AMD code need a compatible runtime, bundler, or migration layer.&lt;/p&gt;
&lt;h3&gt;Exporting in CommonJS&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;module.exports = 1;  
module.exports = { foo: &apos;bar&apos; };  
module.exports = [&apos;foo&apos;, &apos;bar&apos;];  
module.exports = function bar () {};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Exporting in ES6&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Named Exports:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;export function multiply (x, y) {  
  return x * y;  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As well as &lt;strong&gt;exporting a list&lt;/strong&gt; of objects:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function add (x, y) {  
  return x + y;  
};  

function multiply (x, y) {  
  return x * y;  
};  

export { add, multiply };&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Default export:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In our module, we can have many named exports, but we can also have a default export. It’s because our module could be a large library and with default export we can import then an entire module.&lt;/p&gt;
&lt;p&gt;Important to note that there’s only &lt;code class=&quot;language-text&quot;&gt;one default export per module&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;export default function (x, y) {  
  return x * y;  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time we don’t have to use curly braces for importing and we have a chance to name imported statement as we wish.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import multiply from &apos;module&apos;;  
// === OR ===  
import whatever from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A module can have both named exports and a default export:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// module.js  
export function add (x, y) {  
  return x + y;  
};  
export default function (x, y) {  
  return x * y;  
};  

// app.js  
import multiply, { add } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The default export is just a named export with the special name default.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// module.js  
export default function (x, y) {  
  return x * y;  
};  

// app.js  
import multiply from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Importing in ES6&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import { add } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can even import many statements:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import { add, multiply } from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Imports may also be &lt;strong&gt;aliased&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import {   
  add as addition,   
  multiply as multiplication  
} from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and use wildcard (&lt;code class=&quot;language-text&quot;&gt;*&lt;/code&gt;) to import all exported statemets:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import * as module from &apos;module&apos;;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Promises]]></title><description><![CDATA[Promises are one of the most exciting additions to JavaScript ES6. Promises are a pattern that greatly simplifies asynchronous programming by making the code look synchronous and avoid problems associated with callbacks.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-promises/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-promises/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_AqkCUN-kD_fLefEFPnX2Uw.png&quot; alt=&quot;ES6 Promises&quot;&gt;&lt;/p&gt;
&lt;p&gt;Promises are one of the most exciting additions to JavaScript ES6. Promises are a pattern that greatly simplifies asynchronous programming by making the code look synchronous and avoid problems associated with callbacks.&lt;/p&gt;
&lt;p&gt;Prior to ES6, we used &lt;a href=&quot;https://github.com/petkaantonov/bluebird&quot;&gt;bluebird&lt;/a&gt; or &lt;a href=&quot;https://github.com/kriskowal/q&quot;&gt;Q&lt;/a&gt;. Now we have Promises natively.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;resolve&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;reject&lt;/code&gt; are functions themselves and are used to send back values to the promise object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const myPromise = new Promise((resolve, reject) =&gt; {  
    if (Math.random() * 100 &amp;lt;= 90) {  
        resolve(&apos;Hello, Promises!&apos;);  
    }  
    reject(new Error(&apos;In 10% of the cases, I fail. Miserably.&apos;));  
});  

myPromise.then((resolvedValue) =&gt; {  
    console.log(resolvedValue); //Hello, Promises!  
}, (error) =&gt; {  
    console.log(error); //In 10% of the cases, I fail. Miserably.  
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Chaining Promises:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Promises allow us to turn our horizontal code (callback hell):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func1(function (value1) {  
    func2(value1, function (value2) {  
        func3(value2, function (value3) {  
          // Do something with value 3  
        });  
    });  
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Into vertical code like so:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;func1(value1)  
    .then(func2)  
    .then(func3)
    .then(value3 =&gt; {
        // Do something with value 3  
    })
    .catch(error =&gt; {
        // Handle an error from any step
    });&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Parallelize Promises:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can use &lt;code class=&quot;language-text&quot;&gt;Promise.all()&lt;/code&gt; to handle an array of asynchronous operations.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let urls = [  
  &apos;/api/commits&apos;,  
  &apos;/api/issues/opened&apos;,  
  &apos;/api/issues/assigned&apos;,  
  &apos;/api/issues/completed&apos;,  
  &apos;/api/issues/comments&apos;,  
  &apos;/api/pullrequests&apos;  
];  

let promises = urls.map((url) =&gt; {  
  return new Promise((resolve, reject) =&gt; {  
    $.ajax({ url: url })  
      .done((data) =&gt; {  
        resolve(data);  
      });  
  });  
});  

Promise.all(promises)  
  .then((results) =&gt; {  
    // Do something with results of all our promises  
 });&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Set & WeakSet]]></title><description><![CDATA[A Set is a collection for unique values. The values can be primitives or object references.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-set-weakset/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-set-weakset/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_AJDn2sPnvaDVOHZo2F3Zaw.png&quot; alt=&quot;ES6 Set&quot;&gt;&lt;/p&gt;
&lt;p&gt;A &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; is a collection for unique values. The values can be primitives or object references.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let set = new Set();  
set.add(1);  
set.add(&apos;1&apos;);  
set.add({ key: &apos;value&apos; });  
console.log(set); // Set {1, &apos;1&apos;, Object {key: &apos;value&apos;}}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Most importantly is that it does not allow duplicate values, one good use if to remove duplicate values from an array:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[ ...new Set([1, 2, 3, 1, 2, 3]) ] //[1, 2, 3]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Iteration using built-in method forEach and for..of:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// forEach  
let set = new Set([1, &apos;1&apos;, { key: &apos;value&apos; }]);  
set.forEach(function (value) {  
  console.log(value);  
  // 1  
  // &apos;1&apos;  
  // Object {key: &apos;value&apos;}  
});

// for..of  
let set = new Set([1, &apos;1&apos;, { key: &apos;value&apos; }]);  
for (let value of set) {  
  console.log(value);  
  // 1  
  // &apos;1&apos;  
  // Object {key: &apos;value&apos;}  
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Similar to &lt;code class=&quot;language-text&quot;&gt;Map&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; provides us with methods such as &lt;code class=&quot;language-text&quot;&gt;has()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;delete()&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;clear()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;WeakSet&lt;/h3&gt;
&lt;p&gt;Like a &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;WeakSet&lt;/code&gt; is a &lt;code class=&quot;language-text&quot;&gt;Set&lt;/code&gt; that doesn’t prevent its values from being garbage-collected. It has simpler API than &lt;code class=&quot;language-text&quot;&gt;WeakMap&lt;/code&gt;, because has only three methods:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;new WeakSet([iterable])  
WeakSet.prototype.add(value)    : any  
WeakSet.prototype.has(value)    : boolean  
WeakSet.prototype.delete(value) : boolean&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Important thing to note &lt;code class=&quot;language-text&quot;&gt;WeakSet&lt;/code&gt; is a collection that can‘t be iterated and whose size cannot be determined.&lt;/p&gt;
&lt;p&gt;Find more details about &lt;code class=&quot;language-text&quot;&gt;WeakSet&lt;/code&gt; &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet&quot;&gt;&lt;strong&gt;here&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Spread Operator]]></title><description><![CDATA[The spread syntax is simply three dots: `...` It allows an iterable to expand in places where 0+ arguments are expected.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-spread-operator/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-spread-operator/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Sat, 20 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_rA3qD8SBiE_BrmeWuufvYw.png&quot; alt=&quot;ES6 Spread Operator&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Spread Operator&lt;/h3&gt;
&lt;p&gt;The spread syntax is simply three dots: &lt;code class=&quot;language-text&quot;&gt;...&lt;/code&gt; It allows an iterable to expand in places where 0+ arguments are expected.&lt;/p&gt;
&lt;h3&gt;Calling Functions without Apply:&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function doStuff (x, y, z) { }  
var args = [0, 1, 2];  

// Call the function, passing args  
doStuff.apply(null, args);

doStuff(...args);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using spread operator:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const arr = [2, 4, 8, 6, 0];  
const max = Math.max(...arr);  

console.log(max); //8&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or another example using Math functions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let mid = [3, 4];  
let arr = [1, 2, ...mid, 5, 6]; //[1, 2, 3, 4, 5, 6]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Combine arrays&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let arr = [1,2,3];  
let arr2 = [...arr]; // like arr.slice()  
arr2.push(4)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: String Templates]]></title><description><![CDATA[Template Strings use back-ticks (``) rather than the single or double quotes we’re used to with regular strings.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-string-templates/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-string-templates/</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[ES6]]></category><pubDate>Tue, 16 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_OxzGYSWzbivvvMcTkbjC9w.png&quot; alt=&quot;string_templates&quot;&gt;&lt;/p&gt;
&lt;p&gt;Template Strings use back-ticks (``) rather than the single or double quotes we’re used to with regular strings. A template string could thus be written as follows:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const greeting = `Yo World!`;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;String Substitution&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;Substitution allows us to place any valid JavaScript expression inside a Template Literal, the result will be output as part of the same string.&lt;/p&gt;
&lt;p&gt;Template Strings can contain placeholders for string substitution using the ${ } syntax:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var name = &quot;Brendan&quot;;
console.log(`Yo, ${name}!`); //&quot;Yo, Brendan!&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can use expression interpolation to embed for some readable inline math:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var a = 10;
var b = 10;
console.log(`${a+b}`); //20&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;They are also very useful for functions inside expressions:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function fn() { return &quot;inside fn&quot;; }
console.log(`outside, ${fn()}, outside`); // outside, inside fn, outside.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Multiline Strings:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Multiline strings in JavaScript have required hacky workarounds for some time. Template Strings significantly simplify multiline strings. Simply include newlines where they are needed and BOOM.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;let text = `In ES5 this is
not legal.`&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Unescaped template strings:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can now construct strings that have special characters in them without needing to escape them explicitly.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var escapedText = &quot;This string contains \&quot;double quotes\&quot; which are escaped.&quot;;
let templateText = `This string contains &quot;double quotes&quot; which don&apos;t need to be escaped anymore.`;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[ES6 cheatsheet: Variable Declarations]]></title><description><![CDATA[An introduction to ES6 variable declarations, differences between var, let, const.]]></description><link>https://mihaiserban.dev/blog/javascript-es6-cheatsheet-variable-declarations/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/javascript-es6-cheatsheet-variable-declarations/</guid><category><![CDATA[ES6]]></category><category><![CDATA[JavaScript]]></category><pubDate>Tue, 16 Oct 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_T6qcNaaF8T0HedbwF2ZmyQ.png&quot; alt=&quot;es6-variables&quot;&gt;&lt;/p&gt;
&lt;p&gt;ES6 brought &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; with proper lexical scoping. &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; is the new &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;. Constants work just like &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt;, but can’t be reassigned. &lt;code class=&quot;language-text&quot;&gt;let&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt; are block scoped. Therefore, referencing block-scoped identifiers before they are defined will produce a &lt;code class=&quot;language-text&quot;&gt;ReferenceError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Example using &lt;code class=&quot;language-text&quot;&gt;var&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;var variable = 5;

{
  console.log(&apos;inside&apos;, variable); //5
  var variable = 10;
}

console.log(&apos;outside&apos;, variable); //10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example using &lt;code class=&quot;language-text&quot;&gt;const&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const variable = 5;

variable = variable*2; // TypeError: Attempted to assign to readonly property.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Constants are tricky with array and objects. The &lt;code class=&quot;language-text&quot;&gt;reference&lt;/code&gt; becomes constant but the value does not.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const variable = [5];

console.log(variable) // [5]

variable = [2]; //TypeError: Attempted to assign to readonly property.

variable[0] = 1;
console.log(variable) // [1]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find a more complete ES6 cheetsheet on my &lt;a href=&quot;https://github.com/mihaiserban/es6-cheetsheet/blob/master/README.md&quot;&gt;Github&lt;/a&gt; page.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How I grew my Twitter followers from 435 to 1000 in just 12 days 👏👏👏]]></title><description><![CDATA[An experiment on growing my twitter following.]]></description><link>https://mihaiserban.dev/blog/how-i-grew-my-twitter-followers-from-500-to-1000-in-just-12-days/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/how-i-grew-my-twitter-followers-from-500-to-1000-in-just-12-days/</guid><category><![CDATA[Social Media]]></category><category><![CDATA[Growth Hacking]]></category><pubDate>Fri, 11 May 2018 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/1_T1DMihbZ4Pjc7v6UdxvX7w.png&quot; alt=&quot;twitter_stats&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Twitter can sometimes can make you feel like you’re tweeting to a lonesome abyss.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before we dive into the How To, let me tell you that I’m not a social media marketing expert. I’m a software engineer and #socialmedia doesn’t come natural to me 😅.&lt;/p&gt;
&lt;p&gt;My twitter followers have always stagnated at around 300–400. This is mostly because I was just consuming information and didn’t spend time sharing &lt;strong&gt;relevant tweets&lt;/strong&gt; and &lt;strong&gt;building an audience&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Recently I’ve decided to do an experiment and see how far I can take my Twitter account. So let’s get down to it 🧐&lt;/p&gt;
&lt;p&gt;My Twitter followers before doing this experiment: &lt;strong&gt;435&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After doing some research I made a plan:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Setup profile bio, be authenthic, write what your passionate about.&lt;/li&gt;
&lt;li&gt;Add a profile photo.. nobody wants to follow an “egghead” 😒&lt;/li&gt;
&lt;li&gt;Identify my audience. My audience revolves around #swift #objectivec #javascript #graphql #reactjs #nodejs 👍🏻. Use tools such as &lt;a href=&quot;https://hashtagify.me/&quot;&gt;hashtagify.me&lt;/a&gt; to research what hashtags to use.&lt;/li&gt;
&lt;li&gt;Engage audience with relevant tweets. Be consistent! (post 3–6 times a day). Tweeting/retweeting too much will hurt your profile.&lt;/li&gt;
&lt;li&gt;Follow and engage with &lt;strong&gt;active&lt;/strong&gt; people with the same interests as yourself. Search through relevant hasthtags (eg.: #technology) and &lt;strong&gt;engage with the people that actively like and retweet other people’s posts&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Engage in Twitter chats. This can lead to massive exposure.&lt;/li&gt;
&lt;li&gt;PRO TIP: pin your most engaging tweets to your twitter profile. Use &lt;a href=&quot;https://analytics.twitter.com/&quot;&gt;Twitter Analytics&lt;/a&gt; to find your most popular tweets.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to tweet to my twitter audience I’ve setup a workflow using the following tools: &lt;a href=&quot;https://buffer.com/&quot;&gt;Buffer&lt;/a&gt;, &lt;a href=&quot;https://feedly.com/&quot;&gt;Feedly&lt;/a&gt; and &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://feedly.com/&quot;&gt;Feedly&lt;/a&gt; let’s you find and subscribe to quality publications. Also can easily be plugged into services such as &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt; or &lt;a href=&quot;https://ifttt.com/&quot;&gt;IFTTT&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_wTj61rb3GZMKDcURL_aM5w.png&quot; alt=&quot;feedly_screen&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt; allows you to create jobs which check if a new article was published into a Feedly category and then automatically add it to my Buffer social media queue.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_4r3hfl0rWW51jG6CnTsSpQ.png&quot; alt=&quot;zapier_screenshot&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://buffer.com/&quot;&gt;Buffer&lt;/a&gt; is the last piece of the puzzle. The service schedules content to be sent out to my social media profile. TIP: Make sure to keep that queue filled and review it once every couple of days. &lt;strong&gt;Add hashtags, mention authors for extra engagment&lt;/strong&gt; 👌&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;/images/blog/1_aZjiPD1cuhn92DtDVU8Q3g.png&quot; alt=&quot;buffer_screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;One takaway is that consitency is key… Building an audience takes time. Treat your Twitter profile like you were running a marathon, not a sprint.
Let me know if you have any other tips for growing your social media presence 🙏🏻&lt;/p&gt;
&lt;p&gt;See you on &lt;a href=&quot;https://x.com/MihaiSerban&quot;&gt;Twitter&lt;/a&gt;
Happy &lt;em&gt;#GrowthHacking&lt;/em&gt;!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to generate cryptocurrency time intervals using MongoDB Aggregation Framework and Node.js]]></title><description><![CDATA[Group cryptocurrency ticker snapshots into five-minute price summaries with MongoDB, and distinguish rolling-volume changes from traded volume.]]></description><link>https://mihaiserban.dev/blog/aggregate-mongodb-data-with-node-js-and-mongoose-cryptocurrency-financial-time-series/</link><guid isPermaLink="false">https://mihaiserban.dev/blog/aggregate-mongodb-data-with-node-js-and-mongoose-cryptocurrency-financial-time-series/</guid><category><![CDATA[NodeJS]]></category><category><![CDATA[MongoDB]]></category><pubDate>Wed, 23 Aug 2017 22:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/images/blog/0_m4PdT9e8rKaVnWvD.jpeg&quot; alt=&quot;bitcoin&quot;&gt;&lt;/p&gt;
&lt;p&gt;This example groups stored cryptocurrency ticker snapshots into five-minute price summaries using MongoDB&apos;s aggregation pipeline.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Updated 2026-09-13:&lt;/strong&gt; The original pipeline had undefined variables and did not sort ticks before using &lt;code class=&quot;language-text&quot;&gt;$first&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;$last&lt;/code&gt;. The percentage calculation now runs inside the aggregation pipeline. Bitfinex&apos;s ticker &lt;code class=&quot;language-text&quot;&gt;volume&lt;/code&gt; is a rolling 24-hour value, so its change is labeled accordingly rather than presented as volume traded in the interval.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The data here comes from Bitfinex ticker snapshots. A snapshot includes the latest price and rolling 24-hour statistics; it is not a record of every trade. Bitfinex also provides &lt;a href=&quot;https://docs.bitfinex.com/reference/rest-public-candles&quot;&gt;candles&lt;/a&gt; for interval-based market data, so use that endpoint when exchange-generated candles meet your needs.&lt;/p&gt;
&lt;p&gt;Here is the historical ticker payload used in this example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;mid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;244.755&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;bid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;244.75&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;ask&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;244.76&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;last_price&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;244.82&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;low&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;244.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;high&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;248.19&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;volume&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;7842.11542563&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1444253422.348340958&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;low&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;high&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;volume&lt;/code&gt; fields cover a rolling 24-hour window. We’ll group the sampled &lt;code class=&quot;language-text&quot;&gt;last_price&lt;/code&gt; values by time. Missing snapshots can hide price extremes, so the result is a summary of the collected samples rather than a complete trade-based candle.&lt;/p&gt;
&lt;p&gt;Store the data in MongoDB, converting the source timestamp from Unix seconds to a JavaScript &lt;code class=&quot;language-text&quot;&gt;Date&lt;/code&gt; for &lt;code class=&quot;language-text&quot;&gt;created_at&lt;/code&gt;. Otherwise the schema&apos;s default records ingestion time, which can put delayed snapshots into the wrong interval. The snippets below assume an established Mongoose connection.&lt;/p&gt;
&lt;p&gt;Our model looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const mongoose = require(&apos;mongoose&apos;);
const { Schema } = mongoose;

const tickerSchema = new Schema({
  bid: Number,
  bid_size: Number,
  ask: Number,
  ask_size: Number,
  daily_change: Number,
  daily_change_perc: Number,
  last_price: Number,
  volume: Number,
  high: Number,
  low: Number,
  created_at: { type: Date, required: true, default: Date.now },
  symbol: String,
  exchange: String
});

const Ticker = mongoose.model(&apos;Ticker&apos;, tickerSchema);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that we have the data store we can go ahead and create our &lt;a href=&quot;https://docs.mongodb.com/manual/aggregation/&quot;&gt;Mongo Aggregation pipeline&lt;/a&gt; to extract the ticks in time intervals we need.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const pair = &apos;tETHUSD&apos;
const exchange = &apos;Bitfinex&apos;
const periodMinutes = 5; // time interval to process data
const minutesAgo = 30;
let startDate = new Date()
startDate.setMinutes(startDate.getMinutes() - minutesAgo)
const endDate = new Date()
const operations = [
      {
        $match: {
          created_at: {$gte: startDate, $lt: endDate},
          symbol: pair,
          exchange: exchange
        }
      },
      {
        $sort: {
          created_at: 1,
          _id: 1
        }
      },
      {
        $group: {
          _id: {
            $add: [
                { $subtract: [
                    { $subtract: [ &quot;$created_at&quot;, new Date(0) ] },
                    { $mod: [
                        { $subtract: [ &quot;$created_at&quot;, new Date(0) ] },
                        1000 * 60 * periodMinutes
                    ]}
                ]}, new Date(0)]
          },
          first_close: {$first: &quot;$last_price&quot;},
          last_close: {$last: &quot;$last_price&quot;},
          first_volume: {$first: &quot;$volume&quot;},
          last_volume: {$last: &quot;$volume&quot;},
          high: {$max: &quot;$last_price&quot;},
          low: {$min: &quot;$last_price&quot;},
        }
      },
      {
        $project: {
          _id: 1,
          period_change: { $subtract: [ &apos;$last_close&apos;, &apos;$first_close&apos; ] },
          period_change_perc: {
            $cond: [
              { $eq: [&apos;$first_close&apos;, 0] },
              null,
              {
                $multiply: [
                  { $subtract: [{ $divide: [&apos;$last_close&apos;, &apos;$first_close&apos;] }, 1] },
                  100
                ]
              }
            ]
          },
          open: &apos;$first_close&apos;,
          close: &apos;$last_close&apos;,
          rolling_volume_change: { $subtract: [ &apos;$last_volume&apos;, &apos;$first_volume&apos; ] },
          high: &apos;$high&apos;,
          low: &apos;$low&apos;
        }
      },
      {
        $sort: {
          _id: 1
        }
      }
    ];
Ticker.aggregate(operations)
  .then(results =&gt; console.log(results))
  .catch(error =&gt; console.error(error));&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our aggregation pipeline consists of 5 operations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;$match&lt;/strong&gt; : filters the tick data based on date, symbol and exchange&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$sort&lt;/strong&gt; : orders ticks chronologically before &lt;code class=&quot;language-text&quot;&gt;$first&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;$last&lt;/code&gt; are used. &lt;code class=&quot;language-text&quot;&gt;_id&lt;/code&gt; makes ties deterministic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$group&lt;/strong&gt; : groups the data returned by the match operation into periods, in our case 5 minutes. Here we also compute additional value such as the new highs and lows for the periods, first close, last close, first volume, last volume.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$project&lt;/strong&gt; : the data resulting from the &lt;code class=&quot;language-text&quot;&gt;$group&lt;/code&gt; operation is passed into the &lt;code class=&quot;language-text&quot;&gt;$project&lt;/code&gt; phase, where we use &lt;a href=&quot;https://docs.mongodb.com/manual/reference/operator/aggregation/&quot;&gt;operators&lt;/a&gt; to compute the final output. For example, &lt;code class=&quot;language-text&quot;&gt;period_change&lt;/code&gt; is &lt;em&gt;last_close&lt;/em&gt; - &lt;em&gt;first_close&lt;/em&gt;, &lt;code class=&quot;language-text&quot;&gt;period_change_perc&lt;/code&gt; is that change as a percentage of &lt;em&gt;first_close&lt;/em&gt;, and &lt;code class=&quot;language-text&quot;&gt;rolling_volume_change&lt;/code&gt; is the change in the ticker&apos;s rolling 24-hour volume.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;$sort&lt;/strong&gt; : sorts the resulting periods in ascending order&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Output of our aggregation is an array of aggregated tickers at 5 minute intervals:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[ 
  { _id: 2017-08-24T07:15:00.000Z,
    period_change: 0.2400000000000091,
    period_change_perc: 0.07518796992481488,
    open: 319.2,
    close: 319.44,
    rolling_volume_change: -52.8511199200002,
    high: 319.44,
    low: 319.2 },
  { _id: 2017-08-24T07:20:00.000Z,
    period_change: 0.07999999999998408,
    period_change_perc: 0.025043826696714275,
    open: 319.44,
    close: 319.52,
    rolling_volume_change: 18.845093469994026,
    high: 319.52,
    low: 319.44 } 
 ]&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aggregation - MongoDB Manual 3.4&lt;/strong&gt;
&lt;a href=&quot;https://docs.mongodb.com/manual/aggregation/&quot;&gt;Aggregation operations process data records and return computed results.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;General&lt;/strong&gt;
&lt;a href=&quot;https://docs.bitfinex.com/v2/docs/ws-general&quot;&gt;Current Version Bitfinex Websocket API version is 2.0&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>