<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jan paul</title>
    <description>The latest articles on DEV Community by jan paul (@janpauldahlke).</description>
    <link>https://dev.to/janpauldahlke</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F30712%2Ff94df703-9006-4c9c-ae91-e29ef012d70d.jpeg</url>
      <title>DEV Community: jan paul</title>
      <link>https://dev.to/janpauldahlke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/janpauldahlke"/>
    <language>en</language>
    <item>
      <title>How I use GBNF grammars to make small local models stop hallucinating JSON</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Wed, 29 Jul 2026 09:22:43 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/how-i-use-gbnf-grammars-to-make-small-local-models-stop-hallucinating-json-46g</link>
      <guid>https://dev.to/janpauldahlke/how-i-use-gbnf-grammars-to-make-small-local-models-stop-hallucinating-json-46g</guid>
      <description>&lt;p&gt;&lt;em&gt;Or: why your 8B model keeps breaking your agent's tool protocol, and what you can do about it at the token level.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://eris-system.dev/blog/gbnf-grammars" rel="noopener noreferrer"&gt;eris-system.dev/blog/gbnf-grammars&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I've been building &lt;a href="https://github.com/janpauldahlke/eris" rel="noopener noreferrer"&gt;Eris&lt;/a&gt;, a local-first AI agent written in Rust. It sits on your machine, reads and writes an Obsidian-compatible Markdown vault, and calls tools (memory, reminders, web fetch, email, calendar) through a structured JSON protocol. The LLM backend is llama.cpp, the models are 8-26B GGUFs, and nothing leaves your machine unless you explicitly opt in.&lt;/p&gt;

&lt;p&gt;The single hardest engineering problem in the whole project wasn't the orchestrator, the TUI, or the semantic memory layer. It was this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting a small local model to reliably emit valid, schema-conformant JSON.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post is about how I solved it with GBNF grammars (compiled per-session and narrowed per-turn) and why I think this approach generalises to anyone building tool-calling agents on local models.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: small models are sloppy with JSON
&lt;/h2&gt;

&lt;p&gt;If you've tried to build an agent on a local 8B or even 12B model, you know the drill. You tell the model to respond in JSON. It does, mostly. Then on turn 47 it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forgets a closing brace&lt;/li&gt;
&lt;li&gt;Adds a trailing comma in an array&lt;/li&gt;
&lt;li&gt;Invents a tool name that doesn't exist (&lt;code&gt;vault_read&lt;/code&gt; instead of &lt;code&gt;vault:read&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Wraps its JSON in a markdown code fence&lt;/li&gt;
&lt;li&gt;Emits valid JSON followed by a paragraph of conversational text&lt;/li&gt;
&lt;li&gt;Returns &lt;code&gt;"args": "path=notes/today.md"&lt;/code&gt; instead of &lt;code&gt;"args": {"path": "notes/today.md"}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ollama's &lt;code&gt;format: json&lt;/code&gt; helps a bit. It constrains output to &lt;em&gt;some&lt;/em&gt; valid JSON, but it says nothing about &lt;em&gt;which&lt;/em&gt; JSON. Your tool protocol schema isn't enforced. The model can return &lt;code&gt;{"sure": "here is your file"}&lt;/code&gt; and that's perfectly valid JSON. It just isn't a valid tool call.&lt;/p&gt;

&lt;p&gt;OpenAI and Anthropic solve this with server-side function-calling APIs. But we're running locally. We don't have that luxury. We have something better.&lt;/p&gt;

&lt;h2&gt;
  
  
  GBNF: constraining generation at the token level
&lt;/h2&gt;

&lt;p&gt;llama.cpp supports &lt;a href="https://github.com/ggml-org/llama.cpp/blob/master/grammars/README.md" rel="noopener noreferrer"&gt;GBNF grammars&lt;/a&gt;, a BNF-like format that constrains which tokens the sampler is allowed to emit. If a token would produce a string that can't be extended into a valid parse of the grammar, it gets masked out &lt;em&gt;before&lt;/em&gt; sampling. The model literally cannot generate malformed output. Not 'usually doesn't'. Cannot.&lt;/p&gt;

&lt;p&gt;This is a fundamentally different thing from prompt-engineering the model to 'please respond in JSON'. It's a hard constraint on the sampler, enforced at every single token position.&lt;/p&gt;

&lt;p&gt;Eris compiles a GBNF grammar at session start that defines the exact shape of its protocol envelope. Here's the skeleton from &lt;code&gt;src/engine/grammar/envelope.rs&lt;/code&gt;:&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;const&lt;/span&gt; &lt;span class="n"&gt;STATIC_GRAMMAR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;r#"root ::= "{" ws thought-kv "," ws status-kv "," ws message-kv "," ws toolcalls-kv ws "}"

ws ::= [ \t\n]*

thought-kv ::= "\"thought\"" ws ":" ws json-string
status-kv  ::= "\"status\"" ws ":" ws status-enum
message-kv ::= "\"message_to_user\"" ws ":" ws (json-string | "null")
toolcalls-kv ::= "\"tool_calls\"" ws ":" ws "[" ws tool-call-list ws "]"

status-enum ::= "\"Task\"" | "\"Reflect\"" | "\"Idle\"" | "\"Process\""
"#&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every LLM response &lt;em&gt;must&lt;/em&gt; be a JSON object with exactly four keys: &lt;code&gt;thought&lt;/code&gt; (chain-of-thought, always present), &lt;code&gt;status&lt;/code&gt; (one of four enum values), &lt;code&gt;message_to_user&lt;/code&gt; (string or null), and &lt;code&gt;tool_calls&lt;/code&gt; (array of name+args objects). The model can't deviate. It can't add a fifth key. It can't misspell &lt;code&gt;tool_calls&lt;/code&gt;. It can't emit a status value that doesn't exist in the enum.&lt;/p&gt;

&lt;p&gt;The tool call shape itself gets appended depending on whether we're in static mode (any JSON object for args) or dynamic mode (per-tool typed args):&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="cd"&gt;/// Per-tool dynamic-args version: tool-call dispatches into `tool-with-args`.&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;TOOL_CALL_RULES_DYNAMIC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;r#"tool-call-list ::= "" | tool-call ("," ws tool-call)*
tool-call ::= "{" ws "\"name\"" ws ":" ws tool-with-args ws "}"
"#&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice &lt;code&gt;tool-with-args&lt;/code&gt; here. That's where it gets interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling JSON Schema to GBNF rules
&lt;/h2&gt;

&lt;p&gt;Tool arguments are where small models really struggle. A &lt;code&gt;vault:read&lt;/code&gt; call needs &lt;code&gt;{"relative_path": "some/file.md"}&lt;/code&gt;. A &lt;code&gt;web:fetch&lt;/code&gt; call needs &lt;code&gt;{"url": "https://..."}&lt;/code&gt;. Without constraints, the model will happily call &lt;code&gt;vault:read&lt;/code&gt; with &lt;code&gt;{"file": "notes.md"}&lt;/code&gt; (wrong key name, schema validation fails, recovery turn wasted).&lt;/p&gt;

&lt;p&gt;Eris goes further: it compiles each tool's JSON Schema into GBNF rules at startup. The schemas come from Rust structs via &lt;code&gt;schemars&lt;/code&gt;, so the types are the source of truth. Here's the compiler entry point in &lt;code&gt;src/engine/grammar/schema_to_gbnf.rs&lt;/code&gt;:&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;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;schema_to_gbnf_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;schema&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;RootSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GbnfRule&lt;/span&gt;&lt;span class="o"&gt;&amp;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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;rule_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tool_name_to_rule_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CompileCtx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;definitions&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;schema&lt;/span&gt;&lt;span class="py"&gt;.definitions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;extra_rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Vec&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;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compile_schema_object&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;schema&lt;/span&gt;&lt;span class="py"&gt;.schema&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;rule_name&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;ctx&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;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;rule_name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
    &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.extra_rules&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rule_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;GbnfRule&lt;/code&gt; is just a &lt;code&gt;(String, String)&lt;/code&gt; tuple: rule name and rule body. The compiler walks the JSON Schema tree and emits GBNF productions for each type it finds. Objects become key-value sequences with required fields unconditional and optional fields wrapped in &lt;code&gt;(...)?&lt;/code&gt;. Strings with enums become GBNF alternations. Arrays get a helper list rule. &lt;code&gt;$ref&lt;/code&gt; pointers get resolved against the definitions map.&lt;/p&gt;

&lt;p&gt;The object compiler is the meatiest part. It sorts properties (required first, then optional, alphabetically within each group) and emits them with proper comma handling:&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;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ObjectValidation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;parent_rule_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ctx&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;CompileCtx&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... validation checks, depth guard (MAX_DEPTH = 2) ...&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;required_set&lt;/span&gt;&lt;span class="p"&gt;:&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;collections&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;validation&lt;/span&gt;
        &lt;span class="py"&gt;.required&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Sort: required first (alphabetically), then optional (alphabetically).&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;all_fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;FieldInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;required_fields&lt;/span&gt;
        &lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;optional_fields&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.copied&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&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;parts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&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="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_fields&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.enumerate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"\\\"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="se"&gt;\\\"\"&lt;/span&gt;&lt;span class="s"&gt; ws &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="py"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="py"&gt;.expr&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="py"&gt;.required&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&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="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"({kv} )?"&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="n"&gt;field&lt;/span&gt;&lt;span class="py"&gt;.required&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ws &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws {kv}"&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="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(ws &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws {kv} )?"&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;let&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws {} ws &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;}}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So for a Rust struct like this:&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="nd"&gt;#[derive(JsonSchema,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;VaultWriteArgs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;relative_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WriteMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// enum: "overwrite" | "append"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler produces GBNF rules roughly like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vault-write-args ::= "{" ws "\"content\"" ws ":" ws json-string
                      ws "," ws "\"mode\"" ws ":" ws ("\"overwrite\"" | "\"append\"")
                      ws "," ws "\"relative_path\"" ws ":" ws json-string ws "}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the model emits &lt;code&gt;"vault:write"&lt;/code&gt; as the tool name, the sampler &lt;em&gt;forces&lt;/em&gt; the args to conform to &lt;code&gt;vault-write-args&lt;/code&gt;. The model can't use the wrong key. It can't pass a number where a string is required. It can't use a mode value that isn't &lt;code&gt;overwrite&lt;/code&gt; or &lt;code&gt;append&lt;/code&gt;. The schema is literally part of the grammar.&lt;/p&gt;

&lt;p&gt;Arrays work too. For a tool with &lt;code&gt;tags: Vec&amp;lt;String&amp;gt;&lt;/code&gt;, the compiler emits a helper list rule:&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;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ArrayValidation&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;parent_rule_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ctx&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;CompileCtx&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;items_expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;validation&lt;/span&gt;&lt;span class="nf"&gt;.and_then&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="py"&gt;.items&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SingleOrVec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_schema&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;item_schema&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;item_rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{parent_rule_name}-item"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nf"&gt;compile_schema_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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;item_rule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"json-value"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"json-value"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nn"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"json-value"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;list_rule_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{parent_rule_name}-list"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;list_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{items_expr} (&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws {items_expr})*"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.extra_rules&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;list_rule_name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;list_body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; ws ({list_rule_name})? ws &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which produces something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test-array-args-tags-list ::= json-string ("," ws json-string)*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The depth guard (&lt;code&gt;MAX_DEPTH = 2&lt;/code&gt;) prevents the compiler from chasing recursive schemas forever. If a schema is too complex (deeply nested &lt;code&gt;oneOf&lt;/code&gt;, recursive types, free-form &lt;code&gt;additionalProperties&lt;/code&gt; with no fixed keys), the compiler returns &lt;code&gt;None&lt;/code&gt; and the tool falls back to a generic &lt;code&gt;json-object&lt;/code&gt; rule. Graceful degradation, not a crash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-turn grammar narrowing: the real trick
&lt;/h2&gt;

&lt;p&gt;Here's the thing that really moves the needle for small models: Eris doesn't give the model the full 50-tool grammar on every turn.&lt;/p&gt;

&lt;p&gt;Before each LLM call, the orchestrator runs a &lt;strong&gt;ToolRouter&lt;/strong&gt;, a semantic similarity search using the same embedding model as vector memory. The user's message gets embedded and compared against precomputed vectors for each tool (built from tool names, descriptions, and routing hints). Only tools above a cosine similarity threshold make the cut.&lt;/p&gt;

&lt;p&gt;The GBNF grammar is then recompiled to include &lt;em&gt;only those tools&lt;/em&gt;. If the user says 'what time is it', the grammar's &lt;code&gt;tool-with-args&lt;/code&gt; alternation might contain only &lt;code&gt;clock:now&lt;/code&gt;. The model can't call &lt;code&gt;vault:write&lt;/code&gt; even if it wanted to. Those tokens simply aren't available to the sampler.&lt;/p&gt;

&lt;p&gt;The subset cache lives in &lt;code&gt;src/orchestrator/core/llama_gbnf_subset.rs&lt;/code&gt;:&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;pub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_or_compile_subset&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;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;gatekeeper&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;Gatekeeper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tool_names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;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;sorted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_names&lt;/span&gt;&lt;span class="nf"&gt;.to_vec&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;sorted&lt;/span&gt;&lt;span class="nf"&gt;.sort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;sorted&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CACHE_KEY_NO_TOOLS&lt;/span&gt;&lt;span class="nf"&gt;.to_string&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="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;sorted&lt;/span&gt;&lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1e&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// ASCII record separator as cache key&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;guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;FcpError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;EngineFault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GBNF subset cache mutex poisoned"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="nf"&gt;.get&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;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Cache miss: compile grammar for exactly these tools&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ToolGrammarEntry&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sorted&lt;/span&gt;
        &lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;per_tool_rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gatekeeper&lt;/span&gt;
                &lt;span class="nf"&gt;.parameters_root_schema_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.and_then&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nf"&gt;schema_to_gbnf_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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;schema&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|(&lt;/span&gt;&lt;span class="n"&gt;_rule_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)|&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;ToolGrammarEntry&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;per_tool_rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;compiled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compile_fcp_envelope_grammar_dynamic&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;entries&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiled&lt;/span&gt;&lt;span class="nf"&gt;.into_boxed_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arc&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same tool set on a different turn? Cache hit, no recompilation. The &lt;code&gt;Arc&amp;lt;str&amp;gt;&lt;/code&gt; keeps the compiled grammar around cheaply.&lt;/p&gt;

&lt;p&gt;And here's how the orchestrator picks which grammar to use on each LLM call, from the main cognitive loop in &lt;code&gt;src/orchestrator/core/step.rs&lt;/code&gt;:&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grammar_override&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attach_session_grammar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.config&lt;/span&gt;&lt;span class="nf"&gt;.is_llamacpp&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="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Ollama: no grammar support, skip&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;tools_needed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// No tools matched this turn: compile empty grammar (forces tool_calls: [])&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.gbnf_subset_cache&lt;/span&gt;
        &lt;span class="nf"&gt;.get_or_compile_subset&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;self&lt;/span&gt;&lt;span class="py"&gt;.gatekeeper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&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="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;true&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="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;targeted_tools&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Recovery/retry: only the specific tools we're retrying&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;targeted_tools&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.cloned&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.gbnf_subset_cache&lt;/span&gt;
        &lt;span class="nf"&gt;.get_or_compile_subset&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;self&lt;/span&gt;&lt;span class="py"&gt;.gatekeeper&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;names&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="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;true&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="n"&gt;slim_assembly&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Normal path: pre-LLM router's top-K matched tools&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;offered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;slim_offered_tool_names&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;pre_llm_matched_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.tool_map_offer_cap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;moltbook_overlay_latched&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;self&lt;/span&gt;&lt;span class="py"&gt;.gatekeeper&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;self&lt;/span&gt;&lt;span class="py"&gt;.state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;offered&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&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="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// fall back to full session grammar&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.gbnf_subset_cache&lt;/span&gt;
            &lt;span class="nf"&gt;.get_or_compile_subset&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;self&lt;/span&gt;&lt;span class="py"&gt;.gatekeeper&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;offered&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="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;true&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="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// full tool roster, session grammar&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four branches, four different grammar scopes. The &lt;code&gt;!tools_needed&lt;/code&gt; case is fun: when the router decides the user is just chatting (no tools relevant), the grammar still enforces the protocol envelope but compiles with an empty tool list. That means &lt;code&gt;tool_calls&lt;/code&gt; can only be &lt;code&gt;[]&lt;/code&gt;. The model simply cannot hallucinate a tool call when the router says 'this is just conversation'.&lt;/p&gt;

&lt;p&gt;This has two effects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reliability goes way up.&lt;/strong&gt; Fewer choices means fewer mistakes. An 8B model choosing between 3 tools is dramatically more reliable than one choosing between 50.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation is faster.&lt;/strong&gt; A smaller grammar means less work for the constrained sampler at each token position.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The gatekeeper: defence in depth
&lt;/h2&gt;

&lt;p&gt;Grammar enforcement is necessary but not sufficient. Even with a perfect grammar, the model can still call a tool with semantically wrong arguments (a valid path that doesn't exist, or an action that's not appropriate in the current agent state).&lt;/p&gt;

&lt;p&gt;Eris layers a &lt;strong&gt;Gatekeeper&lt;/strong&gt; on top (&lt;code&gt;src/tools/gatekeeper.rs&lt;/code&gt;). Every tool call passes through it before execution:&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;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;execute_tool&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;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;state&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;AgentState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. State-based authorization&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;state_allows_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;FcpError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ToolFault&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tool not authorized in state {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&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;// 2. Normalize common LLM arg-naming mistakes before validation&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize_tool_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Runtime JSON Schema validation (belt and suspenders with grammar)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;compiled_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;JSONSchema&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;options&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.compile&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;schema_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nn"&gt;FcpError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&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;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compiled_schema&lt;/span&gt;&lt;span class="nf"&gt;.validate&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;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;FcpError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;SchemaViolation&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Execute&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool&lt;/span&gt;&lt;span class="nf"&gt;.execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 5. Semantic guard: empty results in Recover state are suspicious&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;AgentState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Recover&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="nf"&gt;.trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;FcpError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ToolFault&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Semantic Guard: Tool returned zero logic results during recovery"&lt;/span&gt;
                &lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The arg normalization is worth a mention. Small models frequently use slightly wrong key names: &lt;code&gt;q&lt;/code&gt; instead of &lt;code&gt;query&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt; instead of &lt;code&gt;relative_path&lt;/code&gt;, &lt;code&gt;top_n&lt;/code&gt; instead of &lt;code&gt;max_headlines&lt;/code&gt;. Rather than wasting a recovery turn, the gatekeeper silently maps common aliases before validation:&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;fn&lt;/span&gt; &lt;span class="nf"&gt;normalize_tool_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="nf"&gt;.as_object_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&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;args&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"web:search"&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;alias&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"search_query"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"search"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.contains_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.retain&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;WEB_SEARCH_KEYS&lt;/span&gt;&lt;span class="nf"&gt;.contains&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;k&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ... similar blocks for web:fetch, web:find, news:today,&lt;/span&gt;
    &lt;span class="c1"&gt;//     vision:see, media:catalog (path -&amp;gt; relative_path) ...&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a pragmatic concession. The grammar enforces &lt;em&gt;structure&lt;/em&gt;. The normalizer handles &lt;em&gt;naming drift&lt;/em&gt;. The schema validator catches whatever slips through both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like in practice
&lt;/h2&gt;

&lt;p&gt;On the Ollama backend (no grammar), a long session with a 12B model hits about 3-5% protocol failures per turn. Malformed JSON, invented tool names, trailing prose after the JSON object. Each failure costs a recovery turn (re-prompt with the parse error), which burns context window and adds latency.&lt;/p&gt;

&lt;p&gt;On the llama.cpp backend with GBNF, protocol failures drop dramatically. Short to medium sessions are essentially clean. But I won't claim zero failures: on long contexts where the model starts losing coherence, even grammar-constrained output can degrade. The grammar guarantees &lt;em&gt;syntactic&lt;/em&gt; validity (the JSON will parse, the keys will be right, the enum values will exist), but it can't fix a model that fills every string field with garbage or repeats the same tool call in a loop because it's lost the plot at 28k tokens.&lt;/p&gt;

&lt;p&gt;That's why Eris doesn't rely on the grammar alone. There's a bounded recovery loop that catches schema violations, empty tool results, and semantic nonsense, feeds the model a concrete correction, and retries with a capped budget. The grammar killed the easy failures (malformed JSON, invented tool names). The recovery loop handles the rest. I'll write more about that mechanism in a follow-up post.&lt;/p&gt;

&lt;p&gt;The difference in session stability is still massive. A 100-turn session on Ollama reliably degrades; the same session on llama.cpp with grammar stays usable far longer. Just don't let anyone tell you it's a silver bullet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bounded generation: the other half
&lt;/h2&gt;

&lt;p&gt;Even with a perfect grammar, generation can go wrong if the model fills its context window mid-response. llama.cpp will truncate, and your valid-so-far JSON becomes invalid because the closing braces never got emitted.&lt;/p&gt;

&lt;p&gt;Eris sets &lt;code&gt;n_predict&lt;/code&gt; (max tokens to generate) per call, calibrated to leave room in the context window for the response to actually finish. The grammar ensures structure; bounded generation ensures the structure has room to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture, briefly
&lt;/h2&gt;

&lt;p&gt;For context on where the grammar fits in the bigger picture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single Rust binary.&lt;/strong&gt; ~270 source files, ~50 tools, three presentation surfaces (ratatui TUI, localhost web UI via Axum+SSE, optional Discord sidecar), all sharing one orchestrator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markdown vault as memory.&lt;/strong&gt; Notes in an Obsidian-compatible directory tree. Tiered memory: ephemeral staging (in-process), committed (written to vault), semantic recall (Qdrant vector search over vault chunks). Turn-start prefetch embeds the user message and injects relevant memories into the system prompt before the LLM even runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No unsafe, no panics, no println.&lt;/strong&gt; Production code uses &lt;code&gt;?&lt;/code&gt; propagation through a typed error taxonomy (&lt;code&gt;FcpError&lt;/code&gt;). Diagnostics go through &lt;code&gt;tracing&lt;/code&gt; to rotating log files under &lt;code&gt;.fcp/telemetry/&lt;/code&gt;. Never to the TUI buffer. &lt;code&gt;println!&lt;/code&gt; would corrupt the ratatui render.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actor model concurrency.&lt;/strong&gt; No &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; shared state between threads (well, except the grammar cache, which is a cold path). Communication via &lt;code&gt;tokio::sync::mpsc&lt;/code&gt; channels.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Should you do this?
&lt;/h2&gt;

&lt;p&gt;If you're building an agent on local models and you need structured output: yes, absolutely. GBNF grammar enforcement is the single biggest reliability win I've made on this project. More impactful than prompt engineering, more impactful than model selection, more impactful than retry logic.&lt;/p&gt;

&lt;p&gt;The key insight is that &lt;strong&gt;the grammar is not a prompt.&lt;/strong&gt; It's not a suggestion. It's a constraint on the sampler. The model can't violate it any more than it can output a token that doesn't exist in its vocabulary. That's a qualitative difference from anything you can do with system prompts.&lt;/p&gt;

&lt;p&gt;The tradeoff: you're coupled to llama.cpp (or any sampler that supports grammars; VLLM has experimental support, Outlines does it differently). You need to write or compile the grammar. And very complex schemas (recursive types, polymorphic unions) may not compile cleanly. But for the typical agent tool protocol (an envelope with a discriminated union of tool calls) it works beautifully.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/janpauldahlke/eris
&lt;span class="nb"&gt;cd &lt;/span&gt;eris &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cargo build &lt;span class="nt"&gt;--release&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6333:6333 &lt;span class="nt"&gt;-p&lt;/span&gt; 6334:6334 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; eris-qdrant-data:/qdrant/storage qdrant/qdrant
./target/release/eris chat   &lt;span class="c"&gt;# first run launches the setup wizard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is at &lt;a href="https://github.com/janpauldahlke/eris" rel="noopener noreferrer"&gt;github.com/janpauldahlke/eris&lt;/a&gt;. The project site is &lt;a href="https://eris-system.dev" rel="noopener noreferrer"&gt;eris-system.dev&lt;/a&gt;. Apache 2.0. Stars and contributions welcome.&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;If you're working on local AI agents, structured generation, or Rust+LLM integration, reach out or &lt;a href="https://github.com/janpauldahlke/eris/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt;.*&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>ai</category>
      <category>machinelearning</category>
      <category>llm</category>
    </item>
    <item>
      <title>thinking to myself when setting up a project boilerplate</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Tue, 04 May 2021 17:33:39 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/what-iam-thing-to-myself-when-setting-up-a-project-boilerplate-1p94</link>
      <guid>https://dev.to/janpauldahlke/what-iam-thing-to-myself-when-setting-up-a-project-boilerplate-1p94</guid>
      <description>&lt;p&gt;this is a &lt;strong&gt;thinking out loud&lt;/strong&gt; to myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  how to prepare a SPA in different frameworks/libraries but also in angular
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ng new&lt;/code&gt; &lt;code&gt;create-react-app&lt;/code&gt; &lt;code&gt;vue cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;enable router&lt;/li&gt;
&lt;li&gt;enable i18n strategy&lt;/li&gt;
&lt;li&gt;enable store / reduxy stuff&lt;/li&gt;
&lt;li&gt;bind router into store&lt;/li&gt;
&lt;li&gt;ensure your API with smthng &lt;a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client"&gt;like&lt;/a&gt;  or &lt;code&gt;curls&lt;/code&gt; / postmany stugff &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;test GET / POST/ UPDATE&lt;/li&gt;
&lt;li&gt;brief moment of think: do we need to auth? if so handle auth token with like setLocalStorage&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;create an entityfolder for selected endpoint, (if angular, also create a module here!!)&lt;/li&gt;
&lt;li&gt;create model/type declartion (only if typescript, which we prefer) of entities&lt;/li&gt;
&lt;li&gt;create service to consume any http-client (eg axios) // consider auth process
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;test GET / POST/ UPDATE &lt;code&gt;loadAll()&lt;/code&gt;, &lt;code&gt;oneOneById()&lt;/code&gt;, 
&lt;code&gt;postNew()&lt;/code&gt;, &lt;code&gt;update()&lt;/code&gt;, &lt;code&gt;delete()&lt;/code&gt; for selected entity&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;test servicemethods in bare minimum but routable component, most liekly in lifecycle method of given framework&lt;/li&gt;
&lt;li&gt;from now on we would need to distungiush from framework to framework/library which should be more articles, &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;in angular -which we atm work with- we go with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create routeresolvers&lt;/li&gt;
&lt;li&gt;prepare actions / reducer / selectors from ngrx, naivly bind http service into the loadaction (we like ngrx-entity here) &lt;/li&gt;
&lt;li&gt;access store in template and trigger action for udf to ensure actions work&lt;/li&gt;
&lt;li&gt;create effect for entity -&amp;gt; &lt;code&gt;loadAllEntity&lt;/code&gt; -&amp;gt; http-service -&amp;gt; dispatch &lt;code&gt;loadAllEntitySucess&lt;/code&gt;, bind result into store&lt;/li&gt;
&lt;li&gt;we forget about, register router, effect, store, store-router in module, considering it silently given from now on, knowing forRoot() and forFeature(). &lt;/li&gt;
&lt;li&gt;remove 3 from template&lt;/li&gt;
&lt;li&gt;consume loadEntity from action in routeresolver -&amp;gt; which should also will trigger the effect -&amp;gt; which will trigger the http -&amp;gt; which will trigger store binding&lt;/li&gt;
&lt;li&gt;bind observable/subscriber to store in template list view&lt;/li&gt;
&lt;li&gt;prepare route for single entity&lt;/li&gt;
&lt;li&gt;access route, write resolver that gains single entity from store / write actions..... from here on&lt;/li&gt;
&lt;li&gt;rinse repaet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;quod erat demonstrandum.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>architecture</category>
      <category>javascript</category>
      <category>boilerplate</category>
    </item>
    <item>
      <title>looking for a good vpn provider</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Sat, 09 Jan 2021 16:14:05 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/looking-for-a-good-vpn-provider-2e53</link>
      <guid>https://dev.to/janpauldahlke/looking-for-a-good-vpn-provider-2e53</guid>
      <description>&lt;p&gt;I just asked this on the Typescript Discord Channel -- Offtopic, but tought why not enhance my question radius.&lt;/p&gt;

&lt;p&gt;[17:08] Paulquapper: could somone suggest my a VPN provider with &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. a documented (javascripty) API and &lt;/li&gt;
&lt;li&gt;2. the feature torenew ip, for given city/country. Ideally non US, no US-Server provider.&lt;/li&gt;
&lt;li&gt;3. "cost efficient" (paying annualy)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>question</category>
      <category>vpn</category>
      <category>devops</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Need help to improve my usage of gatsby-image</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Thu, 19 Nov 2020 10:26:11 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/need-help-to-improve-my-usage-of-gatsby-image-mac</link>
      <guid>https://dev.to/janpauldahlke/need-help-to-improve-my-usage-of-gatsby-image-mac</guid>
      <description>&lt;p&gt;Hello fellow Gatsby'ers. Today i am comming with a question to you. I need to improve my bundle size, and therefore i want to rewrite my gatsby-image wrapper to be more efficient. &lt;/p&gt;

&lt;p&gt;Consider my bundle&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gd_fGVLt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sy22p5f5zxpobphoosxx.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gd_fGVLt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sy22p5f5zxpobphoosxx.PNG" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BUT: the 150 images itself are only 14MB in total, which means gatsby bloats it like crazy.&lt;/p&gt;

&lt;p&gt;I know my code smell in terms of BIG-O, but, my poor excuse, i didn't knew another way than mapping over the result. Can you guys help me to improve here, considering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ImageLarge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StaticQuery&lt;/span&gt;
      &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
        query {
          allFile(filter: { sourceInstanceName: { eq: "assets" } }) {
            edges {
              node {
                relativePath
                name
                childImageSharp {
                  fluid(maxWidth: 600, quality: 100) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
            }
          }
        }
      `&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here now my pile of shame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relativePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&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;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&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="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;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Btw, my problem are comming from not beeing able to parametrize my static gqlquery with arguments, so i took all images, mapped over and returned only the image that i needed. horrible inifficnient. &lt;/p&gt;

&lt;p&gt;Why cant i do something like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ImageLarge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StaticQuery&lt;/span&gt;
      &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
        query {
          allFile(filter: { sourceInstanceName: { eq: `&lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nx"&gt;png&lt;/span&gt;&lt;span class="s2"&gt;` } }) {
            edges {
              node {
                relativePath
                name
                childImageSharp {
                  fluid(maxWidth: 600, quality: 100) {
                    ...GatsbyImageSharpFluid
                  }
                }
              }
            }
          }
        }
      `&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I asked google and found this, which describes my problem perfectly, but offers no solution&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Believe it or not you cannot create a fully dynamic image component using a gastby-image without risking a (possibly very large) bloat in your bundle size. The problem is that static queries in Gatsby do not support string interpolation in it's template literal."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/57098239/what-is-the-best-way-to-display-all-pictures-of-a-directory-in-a-gatsby-project"&gt;what-is-the-best-way-to-display-all-pictures-of-a-directory-in-a-gatsby-project&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If one you could help me to solve this problem, i'd be glad and i also think, that we should write about it here, so others can find this googeling.&lt;/p&gt;

&lt;p&gt;May the code be with you 🙏&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>react</category>
      <category>graphql</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Javascript Homeoffice Stream</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Thu, 12 Nov 2020 09:46:46 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/javascript-homeoffice-stream-5ajk</link>
      <guid>https://dev.to/janpauldahlke/javascript-homeoffice-stream-5ajk</guid>
      <description>&lt;p&gt;Hey fellow nerds,&lt;/p&gt;

&lt;p&gt;out of boredom in the homeOffice era, i started my very "unique" frontend stream. Feel free to join and chit chat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/paulquappe"&gt;https://www.twitch.tv/paulquappe&lt;/a&gt; :-)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to deal with real imposters?</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Thu, 12 Nov 2020 09:43:26 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/how-to-deal-with-real-imposters-5d08</link>
      <guid>https://dev.to/janpauldahlke/how-to-deal-with-real-imposters-5d08</guid>
      <description>&lt;p&gt;Since we all read a lot about imposter syndrom, we tend to see an unrecognized genius in our collegues.&lt;/p&gt;

&lt;p&gt;BUT? How to deal with an actual real imposter in your job?&lt;/p&gt;

&lt;p&gt;I have been hired for a nice project, JAVA Backend + REACT (SSR) Frontend, Webassembly, webGL and Azure, just to bring you some buzzwords. The run is planed for like 5 years, so to say, it is today already planned, what they want to do with the product in the future.&lt;/p&gt;

&lt;p&gt;After 10 years of beeing a developer, mostly Frontend, but also JSP/jinja/smartie and other templating stuff, i decided to take this position for the following reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the stack looks familiar, but also there are some new things&lt;/li&gt;
&lt;li&gt;the lifetime of the project (to comparsion, most agency project last 3 months in coding)&lt;/li&gt;
&lt;li&gt;there will be a new team formed, since we take the project over and make it an internal company project&lt;/li&gt;
&lt;li&gt;my desire to become a senior developer (not only moneywise)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And when they hired me, they said, there will be an IT Team soon in our company. So far, so good.&lt;/p&gt;

&lt;p&gt;Then "the corana" happened and it was decided to hire only the Senior Architect, the HR found a promising candidate. No, they don't liked the indian guys, sadly. Still i  bet he was good at JAVA but bad in german (since iam german). &lt;br&gt;
They offered the job to someone else. It is a handsome, very charming, young and old, 10 years of experience as team-leader "senior software architect".&lt;/p&gt;

&lt;p&gt;Just after stalking him on xing (our german linkedin) i realized, that his buzzwords never fit to each other. he has 4 star experience in &lt;code&gt;C++/C#&lt;/code&gt;!! &lt;/p&gt;

&lt;p&gt;??&lt;br&gt;
!!&lt;/p&gt;

&lt;p&gt;WTF, in either one, or in both? i mean for me, its been worlds between microsoft .NET and C++, but ok...&lt;/p&gt;

&lt;p&gt;Then he has 4 Stars in JAVA/Android Studio? Hu? Java OR android or both or what? Iam confused.&lt;/p&gt;

&lt;p&gt;But, i am the smallest lightbulb in the world, so they hired him, and he gets paid well, since i know my salary, which is pretty ok, but it is not the money i am about.&lt;/p&gt;

&lt;p&gt;After talking to him for an hour, i realized he mixes terminology up. From my experience, if i dont develop daily, skills decay. I forget things, even the &lt;code&gt;any&lt;/code&gt; type in Hypescript (joke is on me).&lt;/p&gt;

&lt;p&gt;I wonder if it is reasonable to think, that someone who has not been in coding for 10 years, since his resumee is saying "PROJEKT MANAGER" and not "DEVELOPER", simply can not code.&lt;/p&gt;

&lt;p&gt;We talked. He didn't know that JAVA servers are configurable for the MEM they are allowed to use. &lt;br&gt;
He has no ****ing clue about JAVA changing it's business model and versioning, openjdk vs oracle java.&lt;/p&gt;

&lt;p&gt;He messes up PUT and POST and sends the same JSON-body on the same route, and asks me after two hours "why is this not working" (we use a swagger).&lt;/p&gt;

&lt;p&gt;So i told him: "please be honest, how good is your JAVA?"&lt;br&gt;
He replied: "i can learn fast, iam just learning spring boot on udemy right now."&lt;br&gt;
And then he said, and this made me really listen up. "In the job description the where looking for a nodejs guy", So his resume has 4 of 5 stars in JAVA/android, but he applied for nodejs?&lt;/p&gt;

&lt;p&gt;JEZ. I get confused about all the wrong buzzwords and project manager gibber he is talking. &lt;/p&gt;

&lt;p&gt;My boss asked me, what i think about my new collegue. What would you say in this moment? I tend to be diplomatic. "He is really nice. I like him. I hope he can show me things in backend, that will make me senior in 4-5 years too."&lt;/p&gt;

&lt;p&gt;I know this differs to the usual dev.to stuff, it's more a writeup. But let me get to the point.&lt;/p&gt;

&lt;p&gt;I think he is an imposter, since he knows what is paid in IT.&lt;/p&gt;

&lt;p&gt;How would you react if you find out your colleque is a liar about his skillset, wirite some tipps in the comments. &lt;/p&gt;

&lt;p&gt;TL:DR&lt;br&gt;
 Among all the smart guys with imposter-syndrome, are some real imposters, for the lulz and the $$$$&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>console.fun() or how to mess up the DOM</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Wed, 13 Mar 2019 19:15:11 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/consolefun-or-how-to-mess-up-the-dom-2kkb</link>
      <guid>https://dev.to/janpauldahlke/consolefun-or-how-to-mess-up-the-dom-2kkb</guid>
      <description>&lt;p&gt;I stumbled upon this little gem in the &lt;a href="https://vue-land.js.org/"&gt;vue-land&lt;/a&gt; #random channel and it made me smile. Posted by &lt;em&gt;xan#2554&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thought maybe it can make you smile too and decided to share it to you. Have fun.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instructions
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;copy script&lt;/li&gt;
&lt;li&gt;visit your most preferred website -- ..most likely &lt;a href="//dev.to"&gt;dev.to&lt;/a&gt; 😜&lt;/li&gt;
&lt;li&gt;open console&lt;/li&gt;
&lt;li&gt;paste script and press &lt;code&gt;enter&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;funnyFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;screenwidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;screenheight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;randT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;screenheight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;randL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;screenwidth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;style&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;transition-duration:500ms; transition-property: left, top; position: fixed; top:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;randT&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px; left:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;randL&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&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;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;funnyFunction&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="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be honest to yourself, did you knew what will happen? &lt;/p&gt;

&lt;p&gt;cheers&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>console</category>
      <category>fun</category>
    </item>
    <item>
      <title>i installed a 9 month old mini project again</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Fri, 09 Nov 2018 09:08:29 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/i-installed-a-9-month-old-mini-project-again-4nbm</link>
      <guid>https://dev.to/janpauldahlke/i-installed-a-9-month-old-mini-project-again-4nbm</guid>
      <description>&lt;p&gt;I installed a npm project again after 9 months...&lt;br&gt;
The package.json contained less then 10 entries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fPMIKX2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/u19Rf6L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fPMIKX2z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/u19Rf6L.png" alt="wtf"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This feels insecure, somehow.... but iam not an expert ;-)&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>npm</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to improve the build speed in React-Typescript, when using material ui</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Mon, 22 Oct 2018 09:44:47 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/how-to-improve-material-ui-speed-in-react-typescript-1199</link>
      <guid>https://dev.to/janpauldahlke/how-to-improve-material-ui-speed-in-react-typescript-1199</guid>
      <description>

&lt;p&gt;Hi,&lt;br&gt;
you are using material - ui in react typescript and having trouble with the performance? Well i stumpled upon the same issue.&lt;br&gt;
The build takes like forever, since i started to use material-ui and i was really no fun to code against such a lame project.&lt;/p&gt;

&lt;p&gt;But lets go in medias res and describe the scenario:&lt;/p&gt;

&lt;p&gt;I wrote a header component like this&lt;br&gt;
&lt;a href="https://i.imgur.com/3pMF0hO.png"&gt;Header&lt;/a&gt;. Pretty staright forward, right?&lt;/p&gt;

&lt;p&gt;While coding i went really unhappy, because the build and the livereolad grew pretty slow. So i played around with the Typescript compiler and found this little command&lt;br&gt;
&lt;code&gt;tsc --diagnostics --listFiles&lt;/code&gt; which i made an alias &lt;code&gt;healtcheck&lt;/code&gt; in my package.json. &lt;/p&gt;

&lt;p&gt;Running it was painfull and ended in results like&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Files:          5598
Lines:        108833
Nodes:        453997
Identifiers:  146625
Symbols:      217251
Types:         56743
Memory used: 300772K
I/O read:      4.66s
I/O write:     0.48s
Parse time:   26.90s
Bind time:     1.62s
Check time:    9.72s
Emit time:     1.64s
Total time:   39.87s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you guys can easily see, it takes quite some time and runs to a TON of files.&lt;/p&gt;

&lt;p&gt;I imported the two icons for the header like this:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {
  AccountCircle,
  Language
} from '@material-ui/icons';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And i was wondering about the mass of files that &lt;code&gt;--diagnostics&lt;/code&gt; considered when building, especially the trillions of &lt;code&gt;/node_modules/@material-ui/icons/**.d.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Yes iam a typescript user, and althought i had some troubles to get into it, i really like it now. As you might know, if a package does not bring its own typings, there is a huge community in &lt;a href="https://github.com/DefinitelyTyped/"&gt;DefinitlyTyped&lt;/a&gt; on github, that provides typings for large packages.&lt;/p&gt;

&lt;p&gt;For example: one would get the typings for material-ui like this: &lt;code&gt;npm install --save-dev @types/material-ui&lt;/code&gt;, with the result of beeing able to consume material-ui in the typescript world.&lt;/p&gt;

&lt;p&gt;But a build time of nearly 40seconds? I wanted to improve this and did some research at big brother.&lt;br&gt;
Found this github &lt;a href="https://github.com/mui-org/material-ui/issues/11916"&gt;issue&lt;/a&gt; that point to the same problem. And i was really happy to read this &lt;a href="https://github.com/mui-org/material-ui/issues/11916#issuecomment-429984950"&gt;comment&lt;/a&gt; with a major improvement.&lt;/p&gt;

&lt;p&gt;Instead of importing like above, i simply rewrote my import of the icons to:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import AccountCircle from '@material-ui/icons/AccountCircle';
import  Language from '@material-ui/icons/Language';
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lets see how much this impacts our &lt;code&gt;healtcheck&lt;/code&gt; now:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Files:           382
Lines:         98396
Nodes:        386176
Identifiers:  130972
Symbols:      201601
Types:         56743
Memory used: 245687K
I/O read:      0.35s
I/O write:     0.37s
Parse time:    4.29s
Bind time:     0.79s
Check time:    5.16s
Emit time:     1.44s
Total time:   11.69s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Wow! Iam was speechless by such a little change, but with an impact like this. We gained nearly 30seconds by altering 2 lines, i mean it could be more lines, but by &lt;strong&gt;NOT IMPORTING VIA THE INDEX&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Iam glad i could share this to you, and it will maybe help the one or another. &lt;/p&gt;

&lt;p&gt;My name is JanPaul and iam working for &lt;a href="http://www.instant-data.com/"&gt;instant-data&lt;/a&gt;. We are small company, that provides internal tooling for large agencies and also data-exchange platforms.&lt;/p&gt;

&lt;p&gt;May the code be with you :pray:&lt;br&gt;
Thanks for the title image to &lt;a href="https://unsplash.com/@emilbruckner"&gt;Emil Bruckner&lt;/a&gt; @unsplash.&lt;/p&gt;


</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>materialui</category>
      <category>react</category>
    </item>
    <item>
      <title>reducks, a boilerplate</title>
      <dc:creator>jan paul</dc:creator>
      <pubDate>Tue, 11 Sep 2018 18:27:54 +0000</pubDate>
      <link>https://dev.to/janpauldahlke/rate-my-boilerplate-j8c</link>
      <guid>https://dev.to/janpauldahlke/rate-my-boilerplate-j8c</guid>
      <description>

&lt;p&gt;&lt;a href="https://unsplash.com/@jaseess"&gt;img by Jase Ess&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;Hello dear dev.to,&lt;/h1&gt;

&lt;p&gt;Iam working for &lt;a href="http://www.instant-data.com/"&gt;instant-data&lt;/a&gt;. a small company that provides solutions in the environment of a larger advertising agency. mostly internal toolings.&lt;/p&gt;

&lt;p&gt;After playing round a little with the create-react-app, i wanted to share my latest boilerplate with you and ask for your opinions, or improvements. You can visit me  &lt;a href="https://github.com/janpauldahlke/react-ts-boilerplate-2018/"&gt;here&lt;/a&gt; on github.&lt;br&gt;
In this i follow the ducks pattern, &lt;b&gt; in which you try to keep all your redux and store reducer stuff in one single file&lt;/b&gt;, but for sakes of DRY, i will present you the README.MD. I appreciate any feedback ;-)&lt;/p&gt;

&lt;p&gt;It is created via the CLI-Tool &lt;br&gt;
&lt;br&gt;
&lt;code&gt;create-react-app &amp;lt;your-appname&amp;gt; --scripts-version=react-scripts-ts&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 and customized to my needs, by running &lt;br&gt;
&lt;br&gt;
&lt;code&gt;npm eject&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h3&gt;Prequisites&lt;/h3&gt;

&lt;p&gt;Installed node and npm Version 8 or higher.&lt;/p&gt;

&lt;h2&gt;Available Scripts&lt;/h2&gt;

&lt;p&gt;In the project directory, you can run:&lt;/p&gt;

&lt;h4&gt;&lt;code&gt;npm start&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Open &lt;a href="http://localhost:3000"&gt;http://localhost:3000&lt;/a&gt; to view it in the browser.&lt;/p&gt;

&lt;h4&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/h4&gt;

&lt;p&gt;Builds the app for production to the &lt;code&gt;build&lt;/code&gt; folder.&lt;/p&gt;

&lt;h4&gt;
&lt;code&gt;npm run seed&lt;/code&gt; Authentication seed&lt;/h4&gt;

&lt;p&gt;To make the boilerplate work from scratch, and to simulate the flow of Auth, we put a JSON-Server.&lt;br&gt;
You should remove the complete seed folder, or better add it to your &lt;code&gt;.gitignore&lt;/code&gt;, when you implement your own workflow. To make use of it simply &lt;code&gt;npm run seed&lt;/code&gt;. You will get served at &lt;code&gt;json-server --port 4000 --watch -- seed/db.json&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;Folder Structure&lt;/h4&gt;

&lt;p&gt;After creation, your project should look like this:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
  README.md
  node_modules/
  package.json
  public/
    index.html
    favicon.ico
  seed/
    /* a helper fake backend for firsttimers */  
  src/
    index.tsx
    components/
      App.tsx
    containers/
      /* your connected containers here */
    ducks/
      /* your ducks here */
    services/
      /* put all your global helpers inside here */
    store/
      history.ts
      index.ts
      localStorage.ts
  types/
    /*your types go here */  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;Debugging and DevTools&lt;/h4&gt;

&lt;p&gt;I strongly suggest you to install and use theses BrowserExtensions.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd"&gt;redux-devtools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi"&gt;react-devtools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Material-UI and Decorators&lt;/h4&gt;

&lt;p&gt;The experimental feature for Decorators is activated in the &lt;code&gt;tsconfig.json&lt;/code&gt;. By this it is possible to use the withStyle decorator in combination with &lt;code&gt;compose&lt;/code&gt; of the &lt;code&gt;recompose package&lt;/code&gt;. &lt;br&gt;
Example usage is:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;compose&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'recompose'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withStyles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'@material-ui/core/styles'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;withStyles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Example&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IExampleProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IExampleState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To make use of themes in material-ui we need to wrap the content of App inside a&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MuiThemeProvider&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createMuiTheme&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c1"&gt;//any content here&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MuiThemeProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://material-ui.com/customization/themes/"&gt;read more about Material UI Themes&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Environment&lt;/h3&gt;

&lt;p&gt;There is the possibility to provide ENV variables. use the &lt;code&gt;.env&lt;/code&gt; file for this.&lt;br&gt;
Please note, they need to follow the naming convention of &lt;code&gt;REACT_APP_&lt;/code&gt; &lt;code&gt;EXAMPLE=FOOBAR&lt;/code&gt;.&lt;br&gt;
Inside the project one can consume them by &lt;code&gt;proccess.env.REACT_APP_EXAMPLE&lt;/code&gt;. &lt;br&gt;
At a new start its set to the seedings path, so one might want to change it there.&lt;/p&gt;

&lt;h3&gt;HTTP Requests&lt;/h3&gt;

&lt;p&gt;Please use axios for your requests. It supports Promises and also async/await.&lt;br&gt;
&lt;a href="https://github.com/axios/axios"&gt;https://github.com/axios/axios&lt;/a&gt;&lt;br&gt;
Also make sure the allow crossorigin-access on your server.&lt;/p&gt;

&lt;h3&gt;Ducks&lt;/h3&gt;

&lt;p&gt;We follow the 'Ducks'-Pattern to structure our application. Get insights on this specc here&lt;br&gt;
&lt;a href="https://github.com/erikras/ducks-modular-redux"&gt;https://github.com/erikras/ducks-modular-redux&lt;/a&gt; &lt;br&gt;
The pattern is to have all these inside one duck:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;action enum&lt;/li&gt;
&lt;li&gt;action types&lt;/li&gt;
&lt;li&gt;action creators&lt;/li&gt;
&lt;li&gt;reducer functions&lt;/li&gt;
&lt;li&gt;mainreducer&lt;/li&gt;
&lt;li&gt;thunk&lt;/li&gt;
&lt;li&gt;initialStore&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Message Component&lt;/h3&gt;

&lt;p&gt;There is a rudimental example prepared in this boilerplate, that shows usage and consumption of a high level message or notification-component. Import the NofictationDuck inside the Ducks that you want the NotificationStore to be catched.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NotificationDuck&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'~/src/ducks/notification'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* success */&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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;NotificationDuck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;throwNotificationWithError&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="s1"&gt;'your text'&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="s1"&gt;'your title'&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But you can also connect them directly to components. Please keep in mind to reset the NotificationStore, with the &lt;code&gt;RESET_NOTIFICATION_STORE&lt;/code&gt; action! &lt;/p&gt;

&lt;h3&gt;Store&lt;/h3&gt;

&lt;p&gt;Use the folder &lt;code&gt;src/store&lt;/code&gt; to configure the store of redux according to your needs. There is also the possibilty to subscribe parts of the store to your localStorage. To do so check the &lt;code&gt;index.ts&lt;/code&gt; and have a brief look at the store subscribe pattern.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;throttle&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;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;RootState&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;AuthStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AuthStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;saveState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;AuthStore&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;RootState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Please make use of the &lt;code&gt;throttle&lt;/code&gt; lodash-helper. &lt;code&gt;JSON.parse()/JSON.stringyfy&lt;/code&gt; are expensive in Javascript.&lt;/p&gt;

&lt;h2&gt;Services&lt;/h2&gt;

&lt;p&gt;Anything you want to consume repetitive belongs to the services. Another word could be globalHelpers.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;apicontroller -&amp;gt; an axois instance the can be configured. import it into your ducks to make &lt;code&gt;http&lt;/code&gt;-verbs.&lt;/li&gt;
&lt;li&gt;authenticationService -&amp;gt; can be used in conjunction with apicontroller inside of ducks to verify auth-Status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;inside your duck - an example usage in thunk&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;'../../services/apicontroller'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;public&lt;/span&gt; &lt;span class="kr"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;getAuth&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="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;:()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;RootState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthDuck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAuthAction&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

      &lt;span class="c1"&gt;//return the axois object with header, pass the state by `getState()` to auth and GET the route with `get('/yourRoute')`&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/Authentication'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&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="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthDuck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAuthSuccessAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&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="cm"&gt;/**
        });
    };
  }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is also a whole &lt;code&gt;react-router&lt;/code&gt; v4 part, i will explain at 50 :heart: :-)&lt;/p&gt;


</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>react</category>
      <category>reducks</category>
    </item>
  </channel>
</rss>
