<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[The wings of a computer engineer]]></title><description><![CDATA[Personal blog for Timothy D Meadows II]]></description><link>https://timothymeadows.com/</link><generator>Ghost 0.11</generator><lastBuildDate>Thu, 04 Jun 2026 05:59:40 GMT</lastBuildDate><atom:link href="https://timothymeadows.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Why I Built OpenCaw: A Shared Instruction Layer for AI-Assisted Development]]></title><description><![CDATA[<p>AI coding tools are getting better fast, but I kept running into the same problem every agent session could behave a little differently.</p>

<p>One session would write tests. Another would skip them. One agent would update architecture notes. Another would leave the knowledge buried in chat history. One session would</p>]]></description><link>https://timothymeadows.com/why-i-built-opencaw/</link><guid isPermaLink="false">38776acb-d751-4edf-8051-e8f8f615d0b1</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Thu, 04 Jun 2026 03:57:33 GMT</pubDate><content:encoded><![CDATA[<p>AI coding tools are getting better fast, but I kept running into the same problem every agent session could behave a little differently.</p>

<p>One session would write tests. Another would skip them. One agent would update architecture notes. Another would leave the knowledge buried in chat history. One session would wait before opening a PR. Another would push ahead without the same level of verification.</p>

<p>That inconsistency is what led me to build <a href="https://github.com/TimothyMeadows/OpenCaw">OpenCaw</a>.</p>

<p>OpenCaw is an open source framework library for AI-assisted development. I designed it to standardize agent instructions, roles, skills, reusable commands, architecture guidance, task tracking, verification, and project memory across tools such as Cursor, Codex, and Claude.</p>

<p>In simpler terms: OpenCaw gives your AI coding agents a consistent way to work. You can use it by speaking naturally about what you want, or you can call specific roles, skills, and commands when you want precision. The explicit names are there for control and repeatability, not because every user should have to memorize them.</p>

<h2 id="whatisopencaw">What Is OpenCaw?</h2>

<p>OpenCaw is not a traditional application framework. It does not dictate whether your app should use .NET, Node, Python, React, Angular, Terraform, Kubernetes, or any other stack.</p>

<p>Instead, OpenCaw is a shared AI development baseline that you mount inside an existing repository.</p>

<p>I built it around a simple idea: the reusable AI operating model should live in one place, while project-specific knowledge should stay inside the project.</p>

<p>OpenCaw gives agents a structured operating model that includes:</p>

<ul>
<li>Role definitions</li>
<li>Skill instructions</li>
<li>Reusable shell commands</li>
<li>Architecture templates</li>
<li>Task tracking conventions</li>
<li>PR readiness rules</li>
<li>Verification workflows</li>
<li>Project-local memory files</li>
</ul>

<p>The mounted OpenCaw folder acts as the shared configuration layer. The host repository keeps its own memory, rules, debug notes, tasks, goals, and project-specific context under <code>.ai/</code>.</p>

<p>That separation matters. I want teams to be able to improve their shared AI workflow without polluting the baseline with one repository's local quirks.</p>

<h2 id="whyibuiltit">Why I Built It</h2>

<p>I created OpenCaw because I wanted AI-assisted development to feel less like improvisation and more like engineering.</p>

<p>Prompting alone is not enough for long-lived repositories. A good prompt can get a useful answer once, but it does not automatically give you repeatable behavior across future sessions, other tools, or other team members.</p>

<p>Without a shared baseline, AI sessions tend to drift:</p>

<ul>
<li>One agent writes tests; another skips them.</li>
<li>One agent updates architecture notes; another ignores them.</li>
<li>One agent opens PRs too early; another waits for confirmation.</li>
<li>One agent stores useful lessons in the wrong place.</li>
<li>One agent understands the stack; another has to rediscover it.</li>
<li>One agent verifies the work; another simply says it is done.</li>
</ul>

<p>OpenCaw is my answer to that drift.</p>

<p>The goal is not to create one magic prompt. The goal is to create a durable operating manual for AI-assisted software work: how agents should plan, implement, verify, document, and hand off changes.</p>

<h2 id="howopencawisstructured">How OpenCaw Is Structured</h2>

<p>I designed OpenCaw to be installed into a repository as a tool directory such as <code>.codex</code>, <code>.cursor</code>, or <code>.claude</code>.</p>

<p>A typical install looks like this:</p>

<pre><code class="language-text">your-repository/  
├── .codex/
│   ├── AGENTS.md
│   ├── .architecture/
│   ├── .roles/
│   ├── commands/
│   └── skills/
├── .ai/
│   ├── MEMORY.md
│   ├── RULES.md
│   ├── DEBUG.md
│   └── tasks/
├── AGENTS.md
└── ARCHITECTURE.md
</code></pre>

<p>The important separation is:  </p>

<table>  
  <thead>
    <tr>
      <th>Area</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>.codex/</code>, <code>.cursor/</code>, or <code>.claude/</code></td>
      <td>Shared OpenCaw baseline</td>
    </tr>
    <tr>
      <td><code>.ai/</code></td>
      <td>Project-local memory, rules, debug notes, tasks, goals, and reports</td>
    </tr>
    <tr>
      <td><code>AGENTS.md</code></td>
      <td>Host repository bootstrap that tells the agent to load OpenCaw</td>
    </tr>
    <tr>
      <td><code>ARCHITECTURE.md</code></td>
      <td>Canonical architecture contract for the host repository</td>
    </tr>
  </tbody>
</table>

<p>OpenCaw includes architecture templates under <code>.architecture/</code>, role definitions under <code>.roles/</code>, reusable task instructions under <code>skills/</code>, and shell automation under <code>commands/</code>.</p>

<p>The repository includes templates and conventions for many common engineering areas, including .NET, Node, Python, Playwright, React, Angular, Vue, Terraform, Kubernetes, Helm, GitHub Actions, Azure DevOps, Docker, databases, and more.</p>

<h2 id="howtoinstallopencaw">How to Install OpenCaw</h2>

<p>Before installing OpenCaw into a project, I recommend forking the repository.</p>

<p>You can install directly from the public repository, but using your own fork gives your team control over updates, local customizations, release pinning, and upstream merges.</p>

<p>In the examples below, replace <code>&lt;your-org&gt;</code> with your GitHub user or organization.</p>

<h3 id="option1installasagitsubmodule">Option 1: Install as a Git Submodule</h3>

<p>This is the approach I recommend for most teams.</p>

<p>Use <code>.codex</code> if you want OpenCaw available to Codex-style agents:</p>

<pre><code class="language-bash">git submodule add https://github.com/&lt;your-org&gt;/OpenCaw .codex  
git submodule update --init --recursive  
</code></pre>

<p>Use <code>.cursor</code> if you want OpenCaw available to Cursor:</p>

<pre><code class="language-bash">git submodule add https://github.com/&lt;your-org&gt;/OpenCaw .cursor  
git submodule update --init --recursive  
</code></pre>

<p>Use <code>.claude</code> if you want OpenCaw available to Claude:</p>

<pre><code class="language-bash">git submodule add https://github.com/&lt;your-org&gt;/OpenCaw .claude  
git submodule update --init --recursive  
</code></pre>

<p>A submodule is useful because your host repository can pin a known-good OpenCaw revision while still allowing controlled updates later.</p>

<h3 id="option2installasaclone">Option 2: Install as a Clone</h3>

<p>Use a plain clone when the repository needs a heavily customized copy of OpenCaw:</p>

<pre><code class="language-bash">git clone https://github.com/&lt;your-org&gt;/OpenCaw .codex  
</code></pre>

<p>Or:</p>

<pre><code class="language-bash">git clone https://github.com/&lt;your-org&gt;/OpenCaw .cursor  
</code></pre>

<p>Or:</p>

<pre><code class="language-bash">git clone https://github.com/&lt;your-org&gt;/OpenCaw .claude  
</code></pre>

<p>This is easier to modify directly, but it also makes upstream updates less structured than a submodule.</p>

<h2 id="howirecommendusingopencaw">How I Recommend Using OpenCaw</h2>

<p>Once installed, OpenCaw should load through the repository's <code>AGENTS.md</code> bootstrap.</p>

<p>If your agent session does not appear to pick it up, explicitly ask it to read the instructions:</p>

<pre><code class="language-text">Read AGENTS.md instructions  
</code></pre>

<p>From there, the most important thing to know is this: you do not have to memorize every role, skill, command, task convention, or goal-flow phrase.</p>

<p>I include exact role names, skill names, and command paths because they are useful. They make workflows auditable, scriptable, repeatable, and easier to debug. But OpenCaw is also designed to work from normal human intent.</p>

<p>You can say what you want in plain English:</p>

<pre><code class="language-text">Review this project for security issues, create tasks for anything serious, and run whatever validation makes sense before summarizing the results.  
</code></pre>

<p>OpenCaw gives the agent enough structure to translate that into the right operating mode: likely a security role, task creation, issue management if relevant, validation commands, and an evidence-based final summary.</p>

<p>That means there are two equally valid ways to use OpenCaw:</p>

<table>  
  <thead>
    <tr>
      <th>Method</th>
      <th>What It Looks Like</th>
      <th>Best For</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Plain-language intent</td>
      <td>
        <code>Review this API for security and reliability, create tasks for the high-risk items, and verify your findings.</code>
      </td>
      <td>
        Everyday usage, onboarding, exploratory work, and users who do not want to memorize syntax
      </td>
    </tr>
    <tr>
      <td>Explicit controls</td>
      <td>
        <code>use role security-engineer + sre, use skill create-task-file, then run the relevant validation commands</code>
      </td>
      <td>
        Repeatable workflows, advanced users, debugging, automation, and team conventions
      </td>
    </tr>
  </tbody>
</table>

<p>My recommendation is to start with plain language. Add explicit roles, skills, or command paths only when you need more control.</p>

<h3 id="1startwithintent">1. Start With Intent</h3>

<p>The most natural way to use OpenCaw is to describe the outcome you want.</p>

<p>For example:</p>

<pre><code class="language-text">Help me implement this feature safely. Plan the work, update the code, add tests, run the right checks, and tell me what changed.  
</code></pre>

<p>That is enough for an OpenCaw-aware agent to choose the right workflow. It can infer that this is implementation work, that task planning may be useful, that test and validation behavior matters, and that the final answer should include evidence.</p>

<p>This is the mode I want most people to use day to day. OpenCaw should make the agent easier to direct, not harder.</p>

<h3 id="2useroleswhenyouwantaspecificperspective">2. Use Roles When You Want a Specific Perspective</h3>

<p>Roles tell the agent which perspective to use.</p>

<p>OpenCaw includes roles such as:</p>

<ul>
<li><code>backend-architect</code></li>
<li><code>frontend-developer</code></li>
<li><code>fullstack-engineer</code></li>
<li><code>qa-engineer</code></li>
<li><code>project-manager</code></li>
<li><code>security-engineer</code></li>
<li><code>sre</code></li>
<li><code>devops-automator</code></li>
<li><code>data-engineer</code></li>
<li><code>technical-writer</code></li>
</ul>

<p>The role index also supports aliases such as <code>security</code>, <code>qa</code>, <code>frontend</code>, <code>backend</code>, <code>devops</code>, <code>sre</code>, and <code>pm</code>.</p>

<p>You can be explicit:</p>

<pre><code class="language-text">use role security-engineer and review this repository for authentication, authorization, and dependency risks  
</code></pre>

<p>Or you can say the same thing naturally:</p>

<pre><code class="language-text">Review this repository like a security engineer. Focus on authentication, authorization, and dependency risks.  
</code></pre>

<p>Both are valid. The explicit version is useful when you want deterministic behavior. The natural-language version is easier to remember.</p>

<h3 id="3composeperspectiveswithoutmemorizingrolenames">3. Compose Perspectives Without Memorizing Role Names</h3>

<p>OpenCaw supports multi-role sessions. The first role acts as the primary perspective. Additional roles add specialist constraints or review lenses.</p>

<p>Explicit version:</p>

<pre><code class="language-text">use role backend-architect + security-engineer and review the payment API design before implementation  
</code></pre>

<p>Plain-language version:</p>

<pre><code class="language-text">Review the payment API design from both an architecture and security perspective before we implement it.  
</code></pre>

<p>I use this pattern often when I want implementation work to carry a second concern, such as quality, security, reliability, or documentation.</p>

<p>You do not have to know the exact role names. Describe the perspectives you want, and OpenCaw gives the agent a structure for mapping those perspectives into its role system.</p>

<h3 id="4letskillsstaybehindthecurtain">4. Let Skills Stay Behind the Curtain</h3>

<p>Skills are reusable instructions for common types of work.</p>

<p>I separate skills from commands on purpose:</p>

<ul>
<li>Skills define the reasoning workflow.</li>
<li>Commands define repeatable execution.</li>
</ul>

<p>You can call skills explicitly:</p>

<pre><code class="language-text">use skill create-task-file + manage-task-issues + test-dotnet  
</code></pre>

<p>But you can also just describe the work:</p>

<pre><code class="language-text">Turn this GitHub issue into a tracked task, implement the fix, link the task back to the issue, and run the relevant .NET tests.  
</code></pre>

<p>That plain-language prompt gives the agent enough signal to use the task-file, issue-management, and .NET testing workflows without requiring you to remember the internal skill names.</p>

<p>Useful skills include:</p>

<table>  
  <thead>
    <tr>
      <th>Skill</th>
      <th>Use It For</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>create-task-file</code></td>
      <td>Creating durable task instructions</td>
    </tr>
    <tr>
      <td><code>manage-task-issues</code></td>
      <td>Syncing task tracking with GitHub issues</td>
    </tr>
    <tr>
      <td><code>orchestrate-subagents</code></td>
      <td>Planning safe parallel work</td>
    </tr>
    <tr>
      <td><code>goal-flow</code></td>
      <td>Coordinating multi-task delivery</td>
    </tr>
    <tr>
      <td><code>test-dotnet</code></td>
      <td>Running .NET verification</td>
    </tr>
    <tr>
      <td><code>playwright-e2e-tests</code></td>
      <td>Browser-level verification</td>
    </tr>
    <tr>
      <td><code>clean-context</code></td>
      <td>Compacting long-running project context</td>
    </tr>
  </tbody>
</table>

<p>Skills make agent behavior less dependent on one-off prompt wording. The skill names are helpful labels, but they are not the only way to trigger the behavior.</p>

<h3 id="5usecommandsforrepeatableexecution">5. Use Commands for Repeatable Execution</h3>

<p>Commands are shell scripts inside the OpenCaw baseline.</p>

<p>Examples:</p>

<pre><code class="language-bash">./.codex/commands/validate-opencaw.sh
./.codex/commands/generate-architecture.sh DOTNET NODE GITHUB_ACTIONS
./.codex/commands/dotnet-build.sh
./.codex/commands/dotnet-test.sh
</code></pre>

<p>I like showing the full command paths because they make it clear what is actually available in the repository. They are useful when you are running something manually, writing documentation, debugging a workflow, or wiring OpenCaw into automation.</p>

<p>But you do not need to remember those paths during normal agent usage.</p>

<p>Instead of saying this:</p>

<pre><code class="language-text">Run ./.codex/commands/dotnet-build.sh and ./.codex/commands/dotnet-test.sh  
</code></pre>

<p>You can say this:</p>

<pre><code class="language-text">Build the .NET project and run the relevant tests before summarizing the result.  
</code></pre>

<p>Instead of saying this:</p>

<pre><code class="language-text">Run ./.codex/commands/generate-architecture.sh DOTNET REACT GITHUB_ACTIONS  
</code></pre>

<p>You can say this:</p>

<pre><code class="language-text">Create an architecture document for this .NET and React project that uses GitHub Actions.  
</code></pre>

<p>OpenCaw includes commands for validation, architecture generation, .NET restore/build/test, Playwright evidence reporting, task files, issue linking, PR readiness, goal files, sub-agent planning, security scans, and database tooling.</p>

<p>The point is not to make users memorize shell scripts. The point is to give the agent reliable tools it can choose when you describe the outcome you want.</p>

<h3 id="6letopencawcreatedurabletaskartifacts">6. Let OpenCaw Create Durable Task Artifacts</h3>

<p>For substantial work, OpenCaw uses <code>.ai/tasks/</code> in the host repository.</p>

<p>A typical task structure looks like this:</p>

<pre><code class="language-text">.ai/tasks/
├── TODO.md
├── OPEN_ISSUES.md
└── checkout-refactor/
    ├── TASK.md
    └── SUBAGENTS.md
</code></pre>

<p>I use this structure because chat history is not enough for serious engineering work.</p>

<p>The task directory gives the repository a durable trail:</p>

<ul>
<li>What the task is</li>
<li>What issue it maps to</li>
<li>What needs to be done</li>
<li>Which sub-agent lanes were planned</li>
<li>What remains open</li>
<li>What validation happened</li>
</ul>

<p>You can request this explicitly:</p>

<pre><code class="language-text">use skill create-task-file and create a task under .ai/tasks for this refactor  
</code></pre>

<p>Or you can say it naturally:</p>

<pre><code class="language-text">This refactor is large enough that I want a durable task plan before implementation.  
</code></pre>

<p>Both should lead the agent toward task planning. The task system exists so work can be paused, resumed, reviewed, and handed off without relying only on chat history.</p>

<h3 id="7usearchitecturetemplatesearly">7. Use Architecture Templates Early</h3>

<p>If your repository does not have an <code>ARCHITECTURE.md</code>, OpenCaw can generate one from selected templates.</p>

<p>Manual command version:</p>

<pre><code class="language-bash">./.codex/commands/generate-architecture.sh DOTNET REACT GITHUB_ACTIONS
</code></pre>

<p>Explicit agent version:</p>

<pre><code class="language-text">use role software-architect and generate ARCHITECTURE.md for this repository using DOTNET, REACT, POSTGRESDB, and GITHUB_ACTIONS templates  
</code></pre>

<p>Plain-language version:</p>

<pre><code class="language-text">Create an architecture document for this repo. It is a .NET backend with a React frontend, Postgres storage, and GitHub Actions for CI.  
</code></pre>

<p>By default, I keep the generated architecture file concise by referencing selected templates instead of inlining all template content. The architecture generation script also supports an <code>--inline</code> option when full embedded template content is explicitly needed.</p>

<p>My recommendation is to create or update architecture documentation early. It gives the agent a contract to preserve instead of forcing every session to rediscover the system from scratch.</p>

<h3 id="8usesubagentplanningonlywhenworkcanbesplitsafely">8. Use Sub-Agent Planning Only When Work Can Be Split Safely</h3>

<p>OpenCaw includes project-manager and sub-agent orchestration support.</p>

<p>Sub-agent planning is useful when different lanes can work independently, such as:</p>

<ul>
<li>One lane investigates current behavior.</li>
<li>One lane updates backend code.</li>
<li>One lane updates frontend code.</li>
<li>One lane writes tests.</li>
<li>One lane reviews security or performance.</li>
</ul>

<p>Explicit version:</p>

<pre><code class="language-text">use 4 agents with project-manager + fullstack-engineer + qa-engineer to split the checkout refactor into safe parallel lanes, keep write sets separate, then integrate and verify  
</code></pre>

<p>Plain-language version:</p>

<pre><code class="language-text">This checkout refactor may be too large for one linear pass. Split it into safe parallel work lanes if that makes sense, keep overlapping file changes to a minimum, then integrate and verify everything together.  
</code></pre>

<p>I do not recommend forcing parallel work just because multiple agents are available.</p>

<p>Good sub-agent planning should keep write sets narrow, avoid overlapping changes, and prefer fewer high-quality lanes over unnecessary fragmentation.</p>

<h3 id="9usegoalflowformultitaskdeliverynotautomerge">9. Use Goal Flow for Multi-Task Delivery, Not Auto-Merge</h3>

<p>Goal flow is for explicit, multi-task automation.</p>

<p>It can move from task to task, raise PRs after validation, run post-PR QA, and produce a final approval report.</p>

<p>It does not mean automatic merge approval, and it does not skip QA.</p>

<p>Explicit version:</p>

<pre><code class="language-text">goal: modernize the reporting module across these five tasks. Automatically raise each task PR after validation, run post-PR QA, then continue to the next task. Do not merge PRs automatically.  
</code></pre>

<p>Plain-language version:</p>

<pre><code class="language-text">I want to work through these five reporting-module tasks as one coordinated goal. For each task, validate the work, open a PR when it is ready, run post-PR QA, and continue to the next task only if the checks pass. Do not merge anything automatically.  
</code></pre>

<p>Use goal flow when you have a clear sequence of tasks and want the agent to manage delivery discipline across them.</p>

<p>Do not use it when requirements are still vague or when each step still needs product approval.</p>

<h2 id="plainlanguageintentvsexplicitcontrols">Plain-Language Intent vs Explicit Controls</h2>

<p>Here is the simplest way to think about it:</p>

<pre><code class="language-text">Tell OpenCaw what outcome you want. Use exact names only when exactness helps.  
</code></pre>

<p>The explicit names are still valuable. I keep them visible because teams need stable vocabulary for docs, automation, task files, pull requests, and repeatable workflows.</p>

<p>But the day-to-day user experience should feel natural.  </p>

<table>  
  <thead>
    <tr>
      <th>You Can Say This</th>
      <th>OpenCaw Can Translate It Into</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>Review this like a security engineer.</code></td>
      <td>A security-focused role and review workflow</td>
    </tr>
    <tr>
      <td><code>Create a durable plan before coding.</code></td>
      <td>Task-file planning under <code>.ai/tasks/</code></td>
    </tr>
    <tr>
      <td><code>This is a multi-step initiative.</code></td>
      <td>Goal-oriented planning and delivery flow</td>
    </tr>
    <tr>
      <td><code>Split this up if parallel work is safe.</code></td>
      <td>Project-manager and sub-agent orchestration behavior</td>
    </tr>
    <tr>
      <td><code>Run the checks that matter for this stack.</code></td>
      <td>Relevant validation, build, test, or evidence commands</td>
    </tr>
    <tr>
      <td><code>Open a PR when it is validated, but do not merge it.</code></td>
      <td>PR readiness flow with a human merge gate</td>
    </tr>
  </tbody>
</table>

<p>This is the balance I wanted OpenCaw to strike: plain-language control for humans, explicit machinery for agents and teams.</p>

<h2 id="exampleprompts">Example Prompts</h2>

<p>Here are practical prompts I use or recommend after installing OpenCaw. Each example is written in plain language first, with an explicit version where the extra precision may help.</p>

<h3 id="repositoryonboarding">Repository Onboarding</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Read the project instructions, inspect this repository, summarize the stack, identify missing architecture documentation, and recommend what should be standardized.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">Read AGENTS.md instructions. Then inspect this repository, summarize the stack, identify missing architecture documentation, and recommend the OpenCaw architecture templates that apply.  
</code></pre>

<h3 id="architecturegeneration">Architecture Generation</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Create an architecture document for this repo. It has a .NET backend, React frontend, Postgres database, and GitHub Actions CI. Keep the document concise and easy for future agents to follow.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">use role software-architect and generate ARCHITECTURE.md using DOTNET, REACT, POSTGRESDB, and GITHUB_ACTIONS. Keep the generated file concise with template read directives.  
</code></pre>

<h3 id="securityreview">Security Review</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Review this repository for security risks. Focus on authentication, authorization, secrets, dependencies, logging, and deployment. Create tracked tasks for high-priority fixes and verify anything you change.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">use role security-engineer + sre and review this repository for authentication, authorization, secrets, dependency, logging, and deployment risks. Create a task file for each high-priority fix.  
</code></pre>

<h3 id="testcoverage">Test Coverage</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Improve test coverage for this feature. Include edge cases, regression coverage, and a clear summary of the verification you ran.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">use role qa-engineer and generate full test coverage for the current feature, including edge cases, regression tests, and verification evidence.  
</code></pre>

<h3 id="fullfeaturedelivery">Full Feature Delivery</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Build the user settings page end to end. Plan the work, implement the API integration, add UI validation, update documentation if needed, run the right checks, and summarize the evidence.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">use role fullstack-engineer and build the user settings page with API integration, UI validation, tests, task tracking, architecture updates if needed, and final verification.  
</code></pre>

<h3 id="existinggithubissue">Existing GitHub Issue</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Work on issue #123. Turn it into a tracked OpenCaw task, implement the fix, link the task back to the issue, add tests, run validation, and prepare it for PR review.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">work on #123. Import the issue into OpenCaw task tracking, implement the fix, add tests, run verification, and prepare PR readiness evidence.  
</code></pre>

<h3 id="parallelrefactor">Parallel Refactor</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">This checkout refactor is large. Split it into safe parallel work lanes only if that reduces risk. Keep file ownership clear, integrate the result, and verify the whole flow before calling it done.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">use 3 agents with project-manager + backend-architect + qa-engineer to plan and implement the checkout refactor. Split only safe parallel lanes, keep write sets separate, then integrate and verify.  
</code></pre>

<h3 id="multitaskgoal">Multi-Task Goal</h3>

<p>Plain-language version:</p>

<pre><code class="language-text">Treat these five reporting tasks as one coordinated goal. Complete them in order, validate each one, open a PR when each task is ready, run post-PR QA, and stop if any check fails. Do not merge automatically.  
</code></pre>

<p>Explicit version:</p>

<pre><code class="language-text">goal: complete the reporting modernization across the five tasks listed in TODO.md. Raise each task PR after validation, run post-PR QA, and stop if validation or QA fails. Do not merge PRs automatically.  
</code></pre>

<h2 id="keepingopencawupdated">Keeping OpenCaw Updated</h2>

<p>If you installed OpenCaw as a submodule, update it with:</p>

<pre><code class="language-bash">git submodule update --remote .codex  
</code></pre>

<p>Or, for another mount name:</p>

<pre><code class="language-bash">git submodule update --remote .cursor  
git submodule update --remote .claude  
</code></pre>

<p>For production repositories, I recommend pinning a known release tag rather than automatically following the latest commit.</p>

<p>Example:</p>

<pre><code class="language-bash">cd .codex  
git fetch --tags  
git checkout 2.0.0  
cd ..  
git add .codex  
git commit -m "chore(opencaw): pin OpenCaw 2.0.0"  
</code></pre>

<p>Replace <code>2.0.0</code> with the release tag you want to pin.</p>

<p>This gives you the best of both worlds: centralized updates when you want them, and controlled changes when stability matters.</p>

<h2 id="bestpractices">Best Practices</h2>

<h3 id="keepopencawsharedkeepmemorylocal">Keep OpenCaw Shared, Keep Memory Local</h3>

<p>Do not put project-specific lessons inside the OpenCaw baseline unless you are intentionally changing the shared framework.</p>

<p>Store project memory in <code>.ai/</code>.</p>

<p>Good places for project-local knowledge:</p>

<pre><code class="language-text">.ai/MEMORY.md
.ai/RULES.md
.ai/DEBUG.md
.ai/FRAGMENTS/
.ai/LEARNINGS/
.ai/tasks/
</code></pre>

<p>This is one of the most important design choices in OpenCaw. The baseline should be reusable. The host repository should own its local truth.</p>

<h3 id="validateafterchangingrolesskillsorcommands">Validate After Changing Roles, Skills, or Commands</h3>

<p>Run validation after editing OpenCaw internals:</p>

<pre><code class="language-bash">./.codex/commands/validate-opencaw.sh
</code></pre>

<p>This is especially important if you customize roles, add skills, or introduce new commands.</p>

<h3 id="preferclearintentovermemorizedsyntax">Prefer Clear Intent Over Memorized Syntax</h3>

<p>Weak prompt:</p>

<pre><code class="language-text">fix this  
</code></pre>

<p>Better plain-language prompt:</p>

<pre><code class="language-text">Fix this bug like a senior developer. Find the root cause, make the smallest safe change, add regression coverage, run the relevant checks, and summarize the evidence.  
</code></pre>

<p>More explicit version:</p>

<pre><code class="language-text">use role senior-developer + qa-engineer and fix this bug. Find the root cause, implement the smallest safe change, add regression coverage, run verification, and summarize the evidence.  
</code></pre>

<p>Both improved prompts give the agent a perspective, a quality bar, and a definition of done. The explicit role name is helpful, but the intent matters more than the syntax.</p>

<h3 id="askforevidencenotjustcompletion">Ask for Evidence, Not Just Completion</h3>

<p>A strong OpenCaw workflow ends with proof.</p>

<p>Good verification requests include:</p>

<pre><code class="language-text">Run the relevant tests and include command output in the final summary.  
</code></pre>

<pre><code class="language-text">Verify the browser flow with Playwright and attach evidence to the PR comment.  
</code></pre>

<pre><code class="language-text">Compare behavior before and after the change and document the difference.  
</code></pre>

<p>I care about evidence because "done" is cheap. Verified is what matters.</p>

<h3 id="usegoalflowcarefully">Use Goal Flow Carefully</h3>

<p>Goal flow is powerful, but it works best when the task sequence is already clear.</p>

<p>Use it for planned delivery, not open-ended discovery.</p>

<p>Good goal-flow prompt:</p>

<pre><code class="language-text">goal: complete the migration plan across the five tasks already listed in TODO.md. Raise each PR after local validation, run post-PR QA, and stop if validation or QA fails. Do not merge PRs automatically.  
</code></pre>

<h3 id="donotskiptheprreadinessgate">Do Not Skip the PR Readiness Gate</h3>

<p>In normal task flow, OpenCaw expects the agent to ask before pushing or opening a PR.</p>

<p>Goal flow is the explicit exception, and even then it still requires validation and post-PR QA.</p>

<p>That gate exists for a reason. AI-assisted development should move quickly, but it should not bypass engineering discipline.</p>

<h2 id="whenopencawisagoodfit">When OpenCaw Is a Good Fit</h2>

<p>OpenCaw is especially useful when:</p>

<ul>
<li>You use AI coding tools across multiple repositories.</li>
<li>Your team wants consistent agent behavior.</li>
<li>You want repeatable architecture and verification standards.</li>
<li>You want durable task tracking instead of chat-only planning.</li>
<li>You want role-based AI workflows for architecture, QA, DevOps, security, and full-stack work.</li>
<li>You want a project memory system that does not pollute the shared baseline.</li>
<li>You want controlled PR readiness and QA behavior.</li>
</ul>

<p>It may be more structure than you need for a tiny throwaway project. But for long-lived repositories, especially team-owned codebases, that structure is the point.</p>

<h2 id="finalthoughts">Final Thoughts</h2>

<p>I built OpenCaw because I wanted AI-assisted development to become more repeatable, more auditable, and more useful across real repositories.</p>

<p>OpenCaw does not replace your coding agent. It gives that agent better rails.</p>

<p>Installed as <code>.codex</code>, <code>.cursor</code>, or <code>.claude</code>, OpenCaw becomes a reusable baseline for how work should be planned, implemented, verified, documented, and handed off.</p>

<p>The result is a more governed AI workflow:</p>

<ul>
<li>Plain-language intent tells the agent what outcome you want.</li>
<li>Roles define perspective when you need precision.</li>
<li>Skills define reasoning workflows behind the scenes.</li>
<li>Commands define repeatable execution.</li>
<li><code>.ai/</code> stores project memory.</li>
<li>Verification evidence keeps the process honest.</li>
</ul>

<p>For teams trying to move from casual AI prompting to repeatable AI-assisted engineering, OpenCaw is the structure I wish I had earlier. That is why I built it.</p>]]></content:encoded></item><item><title><![CDATA[Using Screeps to Learn Enterprise AI Engineering Through Simulation]]></title><description><![CDATA[<p><img src="https://timothymeadows.com/content/images/2026/05/screeps.png" alt=""></p>

<p>Most developers underestimate how transferable enterprise engineering patterns really are.</p>

<p>A queue is still a queue. <br>
A scheduler is still a scheduler. <br>
A planner is still a planner.</p>

<p>Whether the environment is:</p>

<ul>
<li>a Fortune 500 logistics platform,</li>
<li>a cloud-native orchestration system,</li>
<li>an autonomous AI workflow,</li>
<li>or a colony of programmable</li></ul>]]></description><link>https://timothymeadows.com/screeps-and-ai/</link><guid isPermaLink="false">4fbf34e8-b788-423c-81e7-57d0142b312a</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Tue, 12 May 2026 04:54:50 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://timothymeadows.com/content/images/2026/05/screeps.png" alt=""></p>

<p>Most developers underestimate how transferable enterprise engineering patterns really are.</p>

<p>A queue is still a queue. <br>
A scheduler is still a scheduler. <br>
A planner is still a planner.</p>

<p>Whether the environment is:</p>

<ul>
<li>a Fortune 500 logistics platform,</li>
<li>a cloud-native orchestration system,</li>
<li>an autonomous AI workflow,</li>
<li>or a colony of programmable units inside a video game.</li>
</ul>

<p>That is what makes <a href="https://screeps.com/">Screeps</a> uniquely valuable for developers trying to learn modern AI systems engineering.</p>

<p>At first glance, Screeps looks like a programming game.</p>

<p>In reality, it is a persistent distributed systems simulator disguised as a game engine.</p>

<p>More importantly, it allows developers to practice:</p>

<ul>
<li>enterprise software architecture,</li>
<li>AI planning,</li>
<li>autonomous orchestration,</li>
<li>observability,</li>
<li>and systems design,</li>
</ul>

<p>in an environment where the results are visually observable in real time.</p>

<p>That combination is extremely rare.</p>

<hr>

<h1 id="amissingpieceinaieducation">A Missing Piece in AI Education</h1>

<p>Most AI learning material focuses on:</p>

<ul>
<li>prompts,</li>
<li>embeddings,</li>
<li>vector databases,</li>
<li>model APIs,</li>
<li>inference pipelines,</li>
<li>or neural architectures.</li>
</ul>

<p>Very little focuses on:</p>

<ul>
<li>autonomous orchestration,</li>
<li>long-running planning systems,</li>
<li>failure recovery,</li>
<li>distributed coordination,</li>
<li>or AI-native software architecture.</li>
</ul>

<p>But these are the exact problems enterprise teams are struggling with right now.</p>

<p>Modern AI systems increasingly resemble:</p>

<ul>
<li>operating systems,</li>
<li>distributed schedulers,</li>
<li>planner networks,</li>
<li>asynchronous workflow engines,</li>
<li>and event-driven ecosystems.</li>
</ul>

<p>Ironically, Screeps teaches these concepts better than many enterprise tutorials because the systems and their execution become visually observable.</p>

<p>You can literally watch architecture decisions succeed or fail.</p>

<hr>

<h1 id="screepsisclosertoenterprisesoftwarethanmostdevelopersrealize">Screeps Is Closer to Enterprise Software Than Most Developers Realize</h1>

<p>A mature Screeps colony resembles a production enterprise platform far more than a traditional game bot.</p>

<p>Consider the parallels.</p>

<h2 id="enterpriseplatform">Enterprise Platform</h2>

<pre><code class="language-text">API Gateway  
 ├── Authentication Service
 ├── Billing Service
 ├── Notification Service
 ├── Queue Processors
 ├── Workflow Orchestrators
 └── Monitoring Pipelines
</code></pre>

<h2 id="screepscolony">Screeps Colony</h2>

<pre><code class="language-text">Global Colony Manager  
 ├── Spawn Planner
 ├── Logistics Planner
 ├── Harvest Scheduler
 ├── Defense Coordinator
 ├── Expansion Planner
 └── Telemetry System
</code></pre>

<p>Structurally, these are extremely similar systems.</p>

<p>Both involve:</p>

<ul>
<li>asynchronous work,</li>
<li>constrained resources,</li>
<li>event-driven behavior,</li>
<li>state synchronization,</li>
<li>priority management,</li>
<li>fault handling,</li>
<li>distributed execution,</li>
<li>and long-term planning.</li>
</ul>

<p>The difference is that Screeps allows you to <em>see</em> the orchestration.</p>

<hr>

<h1 id="enterprisecodingstandardstranslatedirectlyintoscreeps">Enterprise Coding Standards Translate Directly Into Screeps</h1>

<p>One of the most valuable aspects of using Screeps as an AI engineering sandbox is that enterprise coding patterns map naturally into colony architecture.</p>

<p>This means developers can strengthen both skill sets simultaneously.</p>

<hr>

<h1 id="dependencyinjection">Dependency Injection</h1>

<p>Enterprise systems rely heavily on dependency injection for:</p>

<ul>
<li>modularity,</li>
<li>testing,</li>
<li>abstraction,</li>
<li>and lifecycle management.</li>
</ul>

<p>The same pattern becomes useful in Screeps almost immediately.</p>

<h2 id="hardcodedapproach">Hardcoded Approach</h2>

<pre><code class="language-javascript">const roleHarvester = require('role.harvester');  
</code></pre>

<p>Hardcoded logic scales poorly.</p>

<h2 id="enterpriseinspiredapproach">Enterprise-Inspired Approach</h2>

<pre><code class="language-javascript">class HarvesterBehavior {  
    execute(context) {
        // behavior logic
    }
}
</code></pre>

<pre><code class="language-javascript">container.register("harvester", HarvesterBehavior);  
</code></pre>

<p>Now your colony architecture supports:</p>

<ul>
<li>runtime composition,</li>
<li>strategy replacement,</li>
<li>testing,</li>
<li>behavior injection,</li>
<li>planner abstraction.</li>
</ul>

<p>Exactly like enterprise systems.</p>

<hr>

<h1 id="eventdrivenarchitecture">Event-Driven Architecture</h1>

<p>Enterprise systems increasingly rely on:</p>

<ul>
<li>Kafka,</li>
<li>Azure Service Bus,</li>
<li>RabbitMQ,</li>
<li>event sourcing,</li>
<li>reactive pipelines.</li>
</ul>

<p>Screeps naturally pressures developers toward the same patterns.</p>

<p>Instead of direct execution:</p>

<pre><code class="language-javascript">creep.transfer(storage, RESOURCE_ENERGY);  
</code></pre>

<p>You evolve toward event production:</p>

<pre><code class="language-javascript">eventBus.publish({  
    type: "ENERGY_DELIVERED",
    source: creep.name,
    amount: 100
});
</code></pre>

<p>Now the colony becomes:</p>

<ul>
<li>observable,</li>
<li>decoupled,</li>
<li>replayable,</li>
<li>analyzable.</li>
</ul>

<p>This mirrors modern enterprise architecture almost perfectly.</p>

<hr>

<h1 id="queuesystemsandworkorchestration">Queue Systems and Work Orchestration</h1>

<p>Enterprise orchestration systems live and die by queue management.</p>

<p>So do Screeps colonies.</p>

<p>A naive Screeps colony directly assigns work:</p>

<pre><code class="language-javascript">builder.build(target);  
</code></pre>

<p>A mature colony evolves toward distributed work scheduling:</p>

<pre><code class="language-javascript">taskQueue.enqueue({  
    type: "build",
    priority: 40,
    target: target.id
});
</code></pre>

<p>Suddenly you are implementing:</p>

<ul>
<li>priority queues,</li>
<li>distributed scheduling,</li>
<li>work leasing,</li>
<li>retry handling,</li>
<li>dead-letter concepts,</li>
<li>starvation prevention.</li>
</ul>

<p>These are enterprise engineering problems.</p>

<hr>

<h1 id="aiplanningmapsdirectlytoenterpriseworkflowsystems">AI Planning Maps Directly to Enterprise Workflow Systems</h1>

<p>This is where the connection becomes especially important for modern AI development.</p>

<p>AI systems are increasingly becoming:</p>

<ul>
<li>planner-driven,</li>
<li>goal-oriented,</li>
<li>workflow-based,</li>
<li>and stateful.</li>
</ul>

<p>Screeps naturally teaches these concepts because every colony already behaves like an autonomous workflow engine.</p>

<hr>

<h1 id="goalorientedplanning">Goal-Oriented Planning</h1>

<p>Enterprise AI systems increasingly rely on declarative goals.</p>

<p>Instead of:</p>

<pre><code class="language-text">Run task A.  
Then run task B.  
Then run task C.  
</code></pre>

<p>Modern systems increasingly define desired outcomes:</p>

<pre><code class="language-text">Maintain warehouse inventory above threshold.  
Reduce queue latency below SLA.  
Maintain service health under traffic spikes.  
</code></pre>

<p>Screeps evolves in exactly the same direction.</p>

<h2 id="earlycolonylogic">Early Colony Logic</h2>

<pre><code class="language-javascript">spawnHarvester();  
spawnBuilder();  
spawnUpgrader();  
</code></pre>

<h2 id="maturecolonyplanning">Mature Colony Planning</h2>

<pre><code class="language-javascript">roomGoals = {  
    reserveEnergy: 50000,
    minimumHarvestRate: 120,
    constructionBacklogLimit: 5,
    defenseReadiness: "HIGH"
};
</code></pre>

<p>Now the colony planner determines:</p>

<ul>
<li>what should exist,</li>
<li>when it should exist,</li>
<li>and how to maintain equilibrium.</li>
</ul>

<p>This is extremely similar to enterprise AI orchestration systems.</p>

<hr>

<h1 id="observabilityandtelemetry">Observability and Telemetry</h1>

<p>Enterprise engineering eventually teaches every developer the same painful lesson:</p>

<blockquote>
  <p>If you cannot observe the system, you cannot control the system.</p>
</blockquote>

<p>Screeps teaches this aggressively.</p>

<p>Without telemetry:</p>

<ul>
<li>planners silently fail,</li>
<li>logistics systems oscillate,</li>
<li>CPU budgets collapse,</li>
<li>economies deadlock.</li>
</ul>

<p>This naturally pushes developers toward enterprise observability patterns.</p>

<h2 id="enterprisemonitoringstack">Enterprise Monitoring Stack</h2>

<pre><code class="language-text">Application Insights  
Prometheus  
Grafana  
Elastic  
Distributed tracing  
Structured logging  
</code></pre>

<h2 id="screepsequivalent">Screeps Equivalent</h2>

<pre><code class="language-text">Colony telemetry  
Task diagnostics  
Path heatmaps  
Tick performance metrics  
Planner audit logs  
Economic trend analysis  
</code></pre>

<p>The patterns are nearly identical.</p>

<p>The difference is that Screeps provides immediate visual reinforcement.</p>

<p>You can <em>watch</em> bad telemetry architecture hurt the colony.</p>

<hr>

<h1 id="screepsteachesainativestatemanagement">Screeps Teaches AI-Native State Management</h1>

<p>One of the hardest transitions for enterprise developers entering AI engineering is understanding long-lived state systems.</p>

<p>Traditional enterprise applications are often request-driven:</p>

<pre><code class="language-text">Request → Process → Response  
</code></pre>

<p>AI systems are increasingly persistent:</p>

<pre><code class="language-text">Observe → Plan → Adapt → Persist → Recover  
</code></pre>

<p>Screeps operates entirely in the second model.</p>

<p>Every tick:</p>

<ul>
<li>inherits previous state,</li>
<li>modifies future state,</li>
<li>reacts to changing conditions,</li>
<li>and persists long-term consequences.</li>
</ul>

<p>This mirrors:</p>

<ul>
<li>autonomous agents,</li>
<li>workflow planners,</li>
<li>AI orchestration systems,</li>
<li>robotic coordination systems,</li>
<li>and distributed automation platforms.</li>
</ul>

<hr>

<h1 id="codingstandardsbecomemoreimportantnotless">Coding Standards Become More Important, Not Less</h1>

<p>One misconception around AI-assisted development is that coding standards matter less.</p>

<p>The opposite is true.</p>

<p>AI systems perform dramatically better when:</p>

<ul>
<li>architecture is predictable,</li>
<li>abstractions are stable,</li>
<li>naming conventions are consistent,</li>
<li>planners are isolated,</li>
<li>and system boundaries are clear.</li>
</ul>

<p>Screeps makes this painfully obvious.</p>

<p>A poorly organized colony rapidly becomes impossible for:</p>

<ul>
<li>humans,</li>
<li>planners,</li>
<li>and AI coding systems</li>
</ul>

<p>to reason about.</p>

<p>This naturally reinforces enterprise patterns like:</p>

<ul>
<li>Clean Architecture</li>
<li>CQRS</li>
<li>Domain-Driven Design</li>
<li>Event Sourcing</li>
<li>Service Isolation</li>
<li>Planner Modularization</li>
<li>Strategy Patterns</li>
<li>Finite State Machines</li>
<li>Blackboard Systems</li>
</ul>

<hr>

<h1 id="enterpriserefactoringskillsbecometransferable">Enterprise Refactoring Skills Become Transferable</h1>

<p>One of the most surprising discoveries is how well enterprise refactoring skills translate into Screeps AI systems.</p>

<h2 id="enterpriserefactor">Enterprise Refactor</h2>

<pre><code class="language-text">Break monolith into services.  
</code></pre>

<h2 id="screepsequivalent">Screeps Equivalent</h2>

<pre><code class="language-text">Break colony manager into planners.  
</code></pre>

<hr>

<h2 id="enterpriserefactor">Enterprise Refactor</h2>

<pre><code class="language-text">Introduce queue-based processing.  
</code></pre>

<h2 id="screepsequivalent">Screeps Equivalent</h2>

<pre><code class="language-text">Introduce colony task scheduler.  
</code></pre>

<hr>

<h2 id="enterpriserefactor">Enterprise Refactor</h2>

<pre><code class="language-text">Improve observability and tracing.  
</code></pre>

<h2 id="screepsequivalent">Screeps Equivalent</h2>

<pre><code class="language-text">Add planner telemetry and tick analytics.  
</code></pre>

<p>These are fundamentally the same engineering exercises.</p>

<hr>

<h1 id="aicodingmodelsbecomemoreusefulinscreepsthantraditionalcrud">AI Coding Models Become More Useful in Screeps Than Traditional CRUD</h1>

<p>Large coding models struggle with isolated snippets because there is little systemic context.</p>

<p>But they become extremely effective when reasoning about:</p>

<ul>
<li>planners,</li>
<li>workflows,</li>
<li>scheduling,</li>
<li>coordination,</li>
<li>recovery systems,</li>
<li>and architecture evolution.</li>
</ul>

<p>Screeps gives models exactly the kind of environment where they excel.</p>

<p>Instead of prompting:</p>

<pre><code class="language-text">Generate a CRUD controller.  
</code></pre>

<p>You begin prompting:</p>

<pre><code class="language-text">Refactor this logistics planner to avoid oscillation during energy shortages.

Reduce path recalculation overhead.

Improve colony recovery behavior after catastrophic spawn loss.

Convert direct role assignment into a distributed task queue.  
</code></pre>

<p>These prompts are far closer to real AI engineering problems.</p>

<hr>

<h1 id="safesimulationistherealadvantage">Safe Simulation Is the Real Advantage</h1>

<p>Perhaps the most important aspect of Screeps is this:</p>

<blockquote>
  <p>It is a safe systems laboratory.</p>
</blockquote>

<p>You can experiment with:</p>

<ul>
<li>autonomous planning,</li>
<li>AI-generated code,</li>
<li>aggressive refactors,</li>
<li>distributed coordination,</li>
<li>planner hierarchies,</li>
<li>adaptive scheduling,</li>
<li>and multi-agent behavior</li>
</ul>

<p>without:</p>

<ul>
<li>damaging infrastructure,</li>
<li>risking production systems,</li>
<li>impacting users,</li>
<li>or creating expensive outages.</li>
</ul>

<p>This creates an unusually powerful learning environment.</p>

<p>Especially for enterprise developers transitioning into AI engineering.</p>

<hr>

<h1 id="bridgingenterpriseengineeringandaisystems">Bridging Enterprise Engineering and AI Systems</h1>

<p>This is ultimately why Screeps is so valuable.</p>

<p>It acts as a translation layer between:</p>

<ul>
<li>traditional enterprise software engineering,</li>
<li>and modern autonomous AI architecture.</li>
</ul>

<p>It allows developers to:</p>

<ul>
<li>apply enterprise patterns,</li>
<li>observe autonomous behavior,</li>
<li>test planner architectures,</li>
<li>experiment with AI orchestration,</li>
<li>and visually understand distributed systems dynamics.</li>
</ul>

<p>All inside an environment where failure is educational instead of catastrophic.</p>

<hr>

<h1 id="finalthoughts">Final Thoughts</h1>

<p>Most developers trying to learn AI engineering focus too heavily on:</p>

<ul>
<li>model APIs,</li>
<li>prompts,</li>
<li>or frameworks.</li>
</ul>

<p>But the future of AI systems is not just about inference.</p>

<p>It is about:</p>

<ul>
<li>orchestration,</li>
<li>planning,</li>
<li>state management,</li>
<li>observability,</li>
<li>distributed coordination,</li>
<li>resilience,</li>
<li>and long-running autonomous behavior.</li>
</ul>

<p><a href="https://screeps.com/">Screeps</a> happens to be one of the best environments available for learning all of those skills simultaneously.</p>

<p>Not because it teaches AI directly.</p>

<p>But because it teaches the engineering patterns that modern AI systems increasingly depend on.</p>

<p>The same systems that enterprise engineering teams depend on every day.</p>]]></content:encoded></item><item><title><![CDATA[Arc Raiders - Discord SDK Data Exposure]]></title><description><![CDATA[<h2 id="summary">Summary</h2>

<p>During gameplay of <strong>Arc Raiders</strong>, private Discord Direct Message (DM) conversations between two users were found being written in plaintext to a local game log file. Additionally, a full Discord Bearer authentication token was found stored in the same log file. These findings represent serious privacy and security violations</p>]]></description><link>https://timothymeadows.com/arc-raiders-discord-sdk-data-exposure/</link><guid isPermaLink="false">0f3c0121-8e43-4091-ac62-22e1d4bf688d</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Tue, 03 Mar 2026 17:44:45 GMT</pubDate><content:encoded><![CDATA[<h2 id="summary">Summary</h2>

<p>During gameplay of <strong>Arc Raiders</strong>, private Discord Direct Message (DM) conversations between two users were found being written in plaintext to a local game log file. Additionally, a full Discord Bearer authentication token was found stored in the same log file. These findings represent serious privacy and security violations that affect all players using Discord integration with the game.</p>

<p>Correction:</p>

<p>I originally reported that the bearer token had the ability to send a message on the user behalf. This was in error due to my misunderstanding of the permission <code>rpc.voice.write</code>. This permission only allows the token holder to change the users voice settings. It does not allow them to send a message as the user. This has been corrected in the article below to give the correct abilities. This message serves as a retraction notice.</p>

<p>Update: This has now been patched.</p>

<p><img src="https://timothymeadows.com/content/images/2026/03/discord-embark-confirm.png" alt=""></p>

<hr>

<h2 id="affectedfiles">Affected Files</h2>

<ul>
<li><strong>Log File Location:</strong></li>
</ul>

<pre><code>C:\Users\&lt;username&gt;\AppData\Local\PioneerGame\Saved\Logs  
</code></pre>

<ul>
<li><strong>Log File Name:</strong> <em>discord.log</em></li>
<li><strong>Discord SDK Version:</strong> <em>commit 3b8f3adce7dd1d85463aa700d9185676633e98a1, version 1.8.13395</em></li>
</ul>

<h2 id="risksummary">Risk Summary</h2>

<table>  
  <thead>
    <tr>
      <th>Finding</th>
      <th>Severity</th>
      <th>Users Affected</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Private DM content written to log file</td>
      <td>🔴Critical</td>
      <td>All players using Discord</td>
    </tr>
    <tr>
      <td>Bearer token written to log file</td>
      <td>🔴Critical</td>
      <td>All players using Discord</td>
    </tr>
    <tr>
      <td>Friends list presence data logged</td>
      <td>🟠 High</td>
      <td>All players + their Discord friends</td>
    </tr>
    <tr>
      <td>Overly broad gateway connection scope</td>
      <td>🟠 High</td>
      <td>All players using Discord</td>
    </tr>
  </tbody>
</table>

<hr>

<h3 id="privatedmconversationcontentloggedtodisk">Private DM Conversation Content Logged to Disk</h3>

<p><strong>Description:</strong> <br>
Private Discord Direct Messages exchanged between two users were captured by the game's Discord SDK gateway connection and written in full to a plaintext log file stored locally on the user's machine.</p>

<p><strong>Evidence:</strong> </p>

<ul>
<li><code>MESSAGE_CREATE</code> gateway events appearing in the game log file</li>
<li>Channel type <code>1</code> confirmed - this is a <strong>private DM channel</strong>, not a game or public channel</li>
<li>Messages between users captured in full</li>
<li>Message content, timestamps, user IDs, channel IDs all logged in plaintext</li>
</ul>

<p>The Arc Raiders Discord SDK connects using a full user <strong>Bearer token</strong>, opening a complete Discord gateway connection identical to the one used by the Discord desktop app itself. Discord's gateway pushes <strong>all events</strong> to this connection - including private DM messages. Rather than filtering sensitive events, the SDK logs everything it receives to disk.</p>

<p><strong>Impact:</strong></p>

<ul>
<li>Any private conversation received while the game is running is written to disk<br></li>
<li>Log files may be included in crash reports or bug report uploads<br></li>
<li>Log files may be accessible to other applications on the same machine<br></li>
<li>Third parties with access to the machine or crash reports can read private conversations<br></li>
</ul>

<hr>

<h3 id="discordbearertokenstoredinplaintextlogfile">Discord Bearer Token Stored in Plaintext Log File</h3>

<p><strong>Description:</strong> <br>
The user's full Discord Bearer authentication token was found written in plaintext inside the game log file.</p>

<p><strong>Evidence:</strong>  </p>

<pre><code>"token":"Bearer &lt;redacted&gt;"
</code></pre>

<p><em>(Full token redacted for this report - present in original log file)</em></p>

<p>A Discord Bearer token is functionally equivalent to account access. Anyone in possession of this token can:</p>

<ul>
<li>Read all your messages and DMs</li>
<li>Access your friend list, servers, and account settings</li>
<li>Change voice, or discord settings</li>
<li>Remain logged in until the password is changed</li>
</ul>

<p><strong>Impact:</strong>  </p>

<ul>
<li>If the log file is ever shared (e.g., in a bug report, on a forum, to a support team), the token is fully exposed<br></li>
<li>Crash report systems that automatically upload logs would transmit this token to Embark Studios' servers<br></li>
<li>Malicious software on the same machine could harvest this token from the log file<br></li>
</ul>

<hr>

<h3 id="entirefriendslistpresencedatalogged">Entire Friends List Presence Data Logged</h3>

<p><strong>Description:</strong> <br>
<code>PRESENCE_UPDATE</code> and <code>READY_SUPPLEMENTAL</code> gateway events containing the online/offline status, activity, and metadata of the user's entire Discord friends list were written to the log file.</p>

<p><strong>Evidence:</strong>  </p>

<ul>
<li>Multiple <code>PRESENCE_UPDATE</code> events logged for third-party users<br></li>
<li><code>READY</code> and <code>READY_SUPPLEMENTAL</code> events containing bulk friend presence data at connection time<br></li>
</ul>

<p><strong>Impact:</strong>  </p>

<ul>
<li>Third-party users who have <strong>no relationship with Arc Raiders</strong> have their Discord presence data written to another user's game log file without their knowledge or consent<br></li>
<li>This affects people who never agreed to Arc Raiders' terms of service<br></li>
</ul>

<hr>

<h3 id="broaderthannecessarygatewayscope">Broader Than Necessary Gateway Scope</h3>

<p><strong>Description:</strong> <br>
The Discord SDK integration requests and maintains a <strong>full Discord gateway connection</strong> using the user's Bearer token. Discord's own Rich Presence SDK is designed to only require a limited OAuth scope for game activity display. Using a full gateway connection vastly exceeds what is needed for Rich Presence functionality.</p>

<p><strong>Impact:</strong>  </p>

<ul>
<li>Exposes far more user data than necessary for the stated purpose (showing game status in Discord)<br></li>
<li>Violates the principle of least privilege<br></li>
<li>Creates unnecessary attack surface for all the findings listed above</li>
</ul>

<hr>

<h3 id="forembarkstudiosarcraiders">For Embark Studios / Arc Raiders</h3>

<ol>
<li><strong>Immediately</strong> filter <code>MESSAGE_CREATE</code>, <code>PRESENCE_UPDATE</code>, and authentication events from SDK logging  </li>
<li><strong>Never</strong> log Bearer tokens or authentication credentials to any file  </li>
<li>Reduce the Discord gateway connection scope to only what Rich Presence requires (OAuth, not full Bearer)  </li>
<li>Audit all crash report and log upload systems to ensure tokens and message content are scrubbed before transmission</li>
</ol>

<h3 id="fordiscord">For Discord</h3>

<ol>
<li>Review SDK design that allows full gateway connections via Bearer tokens for third-party games  </li>
<li>Enforce stricter OAuth scopes for game Rich Presence integrations  </li>
<li>Investigate whether this violates Developer Terms of Service</li>
</ol>

<h3 id="foraffectedusersimmediatesteps">For Affected Users (Immediate Steps)</h3>

<ol>
<li><strong>Change your Discord password immediately</strong> - this invalidates your current Bearer token  </li>
<li><strong>Do not share your log files</strong> with anyone, including support teams, until the token is removed  </li>
<li><strong>Disable discord integration</strong> inside of Arc Raiders this will remove the information being written to <code>discord.log</code></li>
</ol>

<p><em>This report was prepared based on direct analysis of game log files. All findings are based on observed technical behavior of the Discord SDK integration within Arc Raiders.</em></p>]]></content:encoded></item><item><title><![CDATA[SBAC: State-Based Access Control]]></title><description><![CDATA[<p>Modern security systems are very good at answering the question:</p>

<blockquote>
  <p>“Is this identity allowed to perform this action?”</p>
</blockquote>

<p>They are far less effective at answering:</p>

<blockquote>
  <p>“Is this action correct <em>right now</em>?”</p>
</blockquote>

<p>Most real-world security failures occur in this gap. Actions are authenticated, authorized, schema-valid, and yet behaviorally wrong. Endpoints are</p>]]></description><link>https://timothymeadows.com/sbac/</link><guid isPermaLink="false">d6f9052d-4068-4a4c-8edd-2d7fe58d4dcf</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Fri, 09 Jan 2026 03:58:30 GMT</pubDate><content:encoded><![CDATA[<p>Modern security systems are very good at answering the question:</p>

<blockquote>
  <p>“Is this identity allowed to perform this action?”</p>
</blockquote>

<p>They are far less effective at answering:</p>

<blockquote>
  <p>“Is this action correct <em>right now</em>?”</p>
</blockquote>

<p>Most real-world security failures occur in this gap. Actions are authenticated, authorized, schema-valid, and yet behaviorally wrong. Endpoints are invoked out of order, workflows are bypassed, state is replayed, and valid APIs are called at invalid times.</p>

<p>I am proposing in this paper that <strong>State-Based Access Control (SBAC)</strong> addresses this gap by enforcing interaction correctness using a server-authoritative model inspired by authoritative simulation systems.</p>

<p><img src="https://timothymeadows.com/content/images/2026/01/sbac.png" alt="sbac"></p>

<p>The diagram above illustrates how SBAC layers alongside RBAC and ABAC. <br>
RBAC answers <em>who</em> may act, ABAC answers <em>under what conditions</em>, and SBAC enforces <em>when an action is valid</em> based on authoritative system state. <br>
Only actions reachable from the current state are permitted. All others are treated as behavioral anomalies.</p>

<hr>

<h1 id="motivationandintuition">Motivation and Intuition</h1>

<h2 id="1thegapbetweenallowedandcorrect">1. The Gap Between Allowed and Correct</h2>

<p>Traditional access control models focus on identity and permission. They assume that once a client is authenticated and authorized, it will behave correctly.</p>

<p>In practice, this assumption fails. Attackers do not need exploits when they can simply call valid endpoints in the wrong order or at the wrong time.</p>

<p>SBAC exists to make behavior itself a first-class security concern.</p>

<hr>

<h2 id="2thecoreprinciple">2. The Core Principle</h2>

<p>SBAC is built on simple concepts:</p>

<ul>
<li><strong>Clients do not execute workflows</strong></li>
<li><strong>They submit inputs</strong></li>
<li><strong>The server decides what is possible</strong></li>
</ul>

<p>This principle has been battle-tested in multiplayer game engines for decades. SBAC applies the same thinking to web applications, APIs, and real-time distributed systems.</p>

<hr>

<h2 id="3whysbaccomplementsrbacandabac">3. Why SBAC Complements RBAC and ABAC</h2>

<p>SBAC is not a replacement for existing access control models.</p>

<ul>
<li>RBAC determines who may act  </li>
<li>ABAC determines under what conditions they may act  </li>
<li>SBAC determines when and in what order actions are valid  </li>
</ul>

<p>All three layers are required for correct enforcement.</p>

<hr>

<h1 id="sbactechnicalspecification">SBAC Technical Specification</h1>

<h2 id="4definitionofstatebasedaccesscontrol">4. Definition of State-Based Access Control</h2>

<p>State-Based Access Control (SBAC) is an access control model in which the validity of an action is determined by the current authoritative state of the system and the set of transitions reachable from that state.</p>

<p>An action is permitted if and only if:</p>

<ol>
<li>The client is authenticated (out of scope)  </li>
<li>The client is authorized by RBAC or ABAC (out of scope)  </li>
<li>The action corresponds to a valid transition from the current state  </li>
<li>All transition guards evaluate to true  </li>
</ol>

<hr>

<h2 id="5formalmodelofinteractionstate">5. Formal Model of Interaction State</h2>

<p>SBAC assumes that applications have security-relevant state, even when that state is implicit in application logic.</p>

<p>This state must be made explicit.</p>

<p>Let:</p>

<ul>
<li><code>S</code> be the set of all security-relevant server states  </li>
<li><code>I</code> be the set of all externally observable client inputs  </li>
<li><code>T ⊆ S × I × S</code> be the set of valid transitions  </li>
</ul>

<p>The authoritative interaction model is defined as:</p>

<pre><code>G = (S, T)  
</code></pre>

<p>This graph is not a developer workflow diagram. <br>
It is a <strong>security artifact</strong>.</p>

<p>If an action is not represented in <code>T</code>, it does not exist from a security perspective.</p>

<h3 id="cexamplemodelingstatesandtransitions">C# example: modeling states and transitions</h3>

<pre><code class="language-csharp">public enum StateId  
{
    Browsing,
    CheckoutPending,
    Complete
}

public enum OperationId  
{
    StartCheckout,
    ConfirmCheckout
}

/// &lt;summary&gt;
/// Canonical external input after transport normalization.
/// &lt;/summary&gt;
public sealed record InputEnvelope(  
    OperationId Operation,
    string ClientId,
    string SessionId,
    long Sequence,
    string Nonce,
    IReadOnlyDictionary&lt;string, string&gt; Meta,
    JsonElement? Payload);

public sealed record Transition(  
    StateId From,
    OperationId Operation,
    StateId To);
</code></pre>

<hr>

<h2 id="6transitionsguardsandreducers">6. Transitions, Guards, and Reducers</h2>

<p>Each transition in <code>T</code> is defined by:</p>

<ul>
<li>a source state  </li>
<li>an operation identifier  </li>
<li>a guard predicate  </li>
<li>a reducer function  </li>
</ul>

<p>The guard determines whether the transition is valid. <br>
The reducer determines how the server state evolves.</p>

<p>Authorization logic belongs in guards. <br>
State mutation belongs in reducers.</p>

<h3 id="cexampleguardandreducerseparation">C# example: guard and reducer separation</h3>

<pre><code class="language-csharp">public delegate ValueTask&lt;bool&gt; GuardAsync(  
    StateId state,
    ClassifiedClient client,
    InputEnvelope input,
    CancellationToken ct);

public delegate ValueTask&lt;StateId&gt; ReducerAsync(  
    StateId state,
    ClassifiedClient client,
    InputEnvelope input,
    CancellationToken ct);

public sealed record TransitionDef(  
    Transition Transition,
    GuardAsync Guard,
    ReducerAsync Reduce);
</code></pre>

<hr>

<h2 id="7inputenvelopesandnormalization">7. Input Envelopes and Normalization</h2>

<p>SBAC does not validate raw requests.</p>

<p>All client interactions are normalized into a canonical input envelope before evaluation.</p>

<p>An input envelope typically contains:</p>

<ul>
<li>operation identifier  </li>
<li>client classification  </li>
<li>monotonic sequence number  </li>
<li>nonce or uniqueness token  </li>
<li>request metadata  </li>
<li>payload (if applicable)  </li>
<li>transport identifier  </li>
</ul>

<p>Normalization ensures uniform enforcement across transports, replay detection, and ordering validation.</p>

<h3 id="cexamplenormalizehttpintoaninputenvelopeminimal">C# example: normalize HTTP into an input envelope (minimal)</h3>

<pre><code class="language-csharp">public static class InputNormalization  
{
    public static InputEnvelope FromHttp(HttpRequest req, string clientId, string sessionId)
    {
        // Example: map endpoint to an SBAC operation
        var op = req.Path.Value switch
        {
            "/checkout/start"   =&gt; OperationId.StartCheckout,
            "/checkout/confirm" =&gt; OperationId.ConfirmCheckout,
            _ =&gt; throw new InvalidOperationException("Unknown operation")
        };

        // Sequence/nonce can be sent as headers or body fields (implementation choice)
        var seq = long.Parse(req.Headers["X-Sbac-Seq"]);
        var nonce = req.Headers["X-Sbac-Nonce"].ToString();

        var meta = new Dictionary&lt;string, string&gt;(StringComparer.OrdinalIgnoreCase)
        {
            ["method"] = req.Method,
            ["path"] = req.Path.Value ?? "",
            ["ua"] = req.Headers.UserAgent.ToString()
        };

        return new InputEnvelope(op, clientId, sessionId, seq, nonce, meta, payload: null);
    }
}
</code></pre>

<hr>

<h2 id="8theghostreachabilityenvelope">8. The Ghost (Reachability Envelope)</h2>

<p>At any moment, only a subset of transitions is valid.</p>

<pre><code>Gs = { t ∈ T | from(t) = s ∧ guard(t, s) = true }  
</code></pre>

<p><code>Gs</code> defines what actions are valid <em>right now</em>. <br>
It is never fully exposed to the client.</p>

<p>Any input outside of <code>Gs</code> is behaviorally inconsistent and constitutes an anomaly.</p>

<p>The ghost represents a strict security boundary. <br>
Transitions outside of <code>Gs</code> are not deferred, sanitized, or conditionally evaluated. <br>
They are categorically invalid for the current state.</p>

<h3 id="cexamplecomputetheghostandenforceit">C# example: compute the ghost and enforce it</h3>

<pre><code class="language-csharp">public sealed class SbacEngine  
{
    private readonly IReadOnlyList&lt;TransitionDef&gt; _defs;

    public SbacEngine(IEnumerable&lt;TransitionDef&gt; defs) =&gt; _defs = defs.ToList();

    public async ValueTask&lt;IReadOnlyList&lt;TransitionDef&gt;&gt; ComputeGhostAsync(
        StateId current,
        ClassifiedClient client,
        InputEnvelope inputContext,
        CancellationToken ct)
    {
        var candidates = _defs.Where(d =&gt; d.Transition.From == current).ToList();
        var allowed = new List&lt;TransitionDef&gt;(capacity: candidates.Count);

        foreach (var d in candidates)
        {
            if (await d.Guard(current, client, inputContext, ct))
                allowed.Add(d);
        }

        return allowed;
    }

    public async ValueTask&lt;StateId&gt; ApplyAsync(
        StateId current,
        ClassifiedClient client,
        InputEnvelope input,
        CancellationToken ct)
    {
        var ghost = await ComputeGhostAsync(current, client, input, ct);

        var match = ghost.FirstOrDefault(d =&gt; d.Transition.Operation == input.Operation);
        if (match is null)
            throw new SbacAnomalyException(SbacAnomaly.InvalidTransitionAttempt, "Operation not in ghost");

        return await match.Reduce(current, client, input, ct);
    }
}
</code></pre>

<hr>

<h2 id="9boundedlookaheadandpredictivedisclosure">9. Bounded Lookahead and Predictive Disclosure</h2>

<p>SBAC may optionally define bounded reachability:</p>

<pre><code>Reachable(s, k)  
</code></pre>

<p>This represents the set of states reachable from <code>s</code> within <code>k</code> transitions.</p>

<p>Bounded reachability enables predictive asset loading, UI prefetching, capability pre-issuance, and latency hiding.</p>

<p>Predictive disclosure must never expand the ghost.</p>

<h3 id="cexampleboundedlookaheadforprefetchhintsconceptual">C# example: bounded lookahead for prefetch hints (conceptual)</h3>

<pre><code class="language-csharp">public static class Reachability  
{
    public static ISet&lt;StateId&gt; ReachableWithin(
        StateId start,
        int k,
        IEnumerable&lt;TransitionDef&gt; defs)
    {
        var byFrom = defs.GroupBy(d =&gt; d.Transition.From)
                         .ToDictionary(g =&gt; g.Key, g =&gt; g.Select(x =&gt; x.Transition.To).ToArray());

        var reachable = new HashSet&lt;StateId&gt; { start };
        var frontier = new HashSet&lt;StateId&gt; { start };

        for (int depth = 0; depth &lt; k; depth++)
        {
            var next = new HashSet&lt;StateId&gt;();
            foreach (var s in frontier)
            {
                if (!byFrom.TryGetValue(s, out var tos)) continue;
                foreach (var to in tos)
                    if (reachable.Add(to)) next.Add(to);
            }
            frontier = next;
            if (frontier.Count == 0) break;
        }

        return reachable;
    }
}
</code></pre>

<hr>

<h2 id="10clientmodel">10. Client Model</h2>

<p>SBAC replaces the ambiguous notion of “user” with <strong>client</strong>.</p>

<p>A client is any external entity that interacts with the system and may originate inputs or receive disclosures.</p>

<p>Client classification is assigned by the server, not claimed by the client.</p>

<h3 id="cexampleclassifyaclientsimplified">C# example: classify a client (simplified)</h3>

<pre><code class="language-csharp">public enum ClientClass  
{
    Human,
    Api,
    Seo,
    Unknown
}

public sealed record ClassifiedClient(  
    string ClientId,
    ClientClass Class,
    string? SubjectId,     // user id or service principal id
    string? TokenId);      // JTI or equivalent
</code></pre>

<hr>

<h2 id="11clientclasses">11. Client Classes</h2>

<p>SBAC recognizes four common client classes:</p>

<ul>
<li>Interactive Human Client  </li>
<li>Programmatic API Client  </li>
<li>Observational Client (SEO)  </li>
<li>Unknown Client  </li>
</ul>

<p>Each class receives a different projection of the system and interaction surface.</p>

<hr>

<h2 id="12authoritativedisclosureseo">12. Authoritative Disclosure (SEO)</h2>

<p>Observational clients do not traverse workflows.</p>

<p>They receive disclosure projections, which are read-only, server-authored views of the system.</p>

<p>Disclosure endpoints must never originate state transitions.</p>

<hr>

<h2 id="13capabilitytokensandstatecoupling">13. Capability Tokens and State Coupling</h2>

<p>SBAC discourages implicit permission for stateful operations.</p>

<p>Instead, the server issues capability tokens that bind an operation to a specific state or resource and are time-limited.</p>

<p>Capabilities are proofs of reachability, not authentication.</p>

<h3 id="cexamplecapabilitytokenshapeconceptual">C# example: capability token shape (conceptual)</h3>

<pre><code class="language-csharp">public sealed record Capability(  
    string CapabilityId,
    string ClientId,
    OperationId Operation,
    StateId BoundState,
    DateTimeOffset ExpiresAt,
    string Signature); // HMAC or asymmetric signature

// Verify that the capability was issued by the server and is still valid
public static bool VerifyCapability(Capability cap, StateId currentState, DateTimeOffset nowUtc)  
{
    if (cap.ExpiresAt &lt;= nowUtc) return false;
    if (cap.BoundState != currentState) return false;
    // Signature validation omitted here (implementation-specific)
    return true;
}
</code></pre>

<hr>

<h2 id="14transportagnosticenforcement">14. Transport-Agnostic Enforcement</h2>

<p>All client inputs must pass through the same authoritative validation pipeline, regardless of transport.</p>

<p>Behavior must be enforced identically across HTTP, WebSockets, SignalR, gRPC, and message queues.</p>

<hr>

<h2 id="15temporalintegrity">15. Temporal Integrity</h2>

<p>SBAC assumes adversaries can replay requests.</p>

<p>The server must therefore detect duplicated, reordered, or delayed inputs using sequence numbers, nonces, or server-issued state versions.</p>

<h3 id="cexamplebasicsequencenoncegatepersession">C# example: basic sequence/nonce gate (per session)</h3>

<pre><code class="language-csharp">public sealed class ReplayGate  
{
    private long _lastSeq;
    private readonly HashSet&lt;string&gt; _nonces = new(StringComparer.Ordinal);

    public bool Accept(long seq, string nonce)
    {
        if (seq &lt;= _lastSeq) return false;
        if (!_nonces.Add(nonce)) return false;

        _lastSeq = seq;

        // Production: expire nonces over time to bound memory.
        return true;
    }
}
</code></pre>

<hr>

<h2 id="16anomalyclassification">16. Anomaly Classification</h2>

<p>SBAC defines a standard anomaly taxonomy:</p>

<ul>
<li>Invalid transition attempt  </li>
<li>Temporal violation  </li>
<li>Guard failure  </li>
<li>Enumeration or probing  </li>
<li>Integrity divergence  </li>
</ul>

<p>Anomalies are first-class security signals.</p>

<p>Anomaly classification is coupled with response policy.</p>

<p>SBAC does not mandate a specific response, but it requires that anomalies be observable and actionable. <br>
Common responses include token revocation, forced re-authentication, progressive trust degradation, and temporary account lockout.</p>

<h3 id="cexampleanomalytypesandexception">C# example: anomaly types and exception</h3>

<pre><code class="language-csharp">public enum SbacAnomaly  
{
    InvalidTransitionAttempt,
    TemporalViolation,
    GuardFailure,
    EnumerationOrProbing,
    IntegrityDivergence
}

public sealed class SbacAnomalyException : Exception  
{
    public SbacAnomaly Type { get; }

    public SbacAnomalyException(SbacAnomaly type, string message) : base(message)
        =&gt; Type = type;
}
</code></pre>

<hr>

<h1 id="exampleblockingarealworldworkflowbypass">Example: Blocking a Real-World Workflow Bypass</h1>

<h2 id="17scenariooverview">17. Scenario Overview</h2>

<p>Consider a common e-commerce checkout flow:</p>

<ol>
<li>Browse catalog  </li>
<li>Select item  </li>
<li>Start checkout  </li>
<li>Confirm payment  </li>
</ol>

<p>The system uses modern authentication (OAuth or JWT-based access tokens), RBAC for role enforcement, and ABAC for contextual checks such as region, device, or account state.</p>

<p>An attacker does not bypass authentication.</p>

<p>Instead, the attacker successfully exploits a client-side vulnerability (for example, reflected XSS in a product review field) and steals a legitimate user’s access token.</p>

<p>The attacker now possesses a valid, unexpired token issued by the system and begins using it programmatically.</p>

<hr>

<h2 id="18attackwithoutsbac">18. Attack Without SBAC</h2>

<p>Using the stolen access token, the attacker:</p>

<ul>
<li>presents a valid bearer token issued to a real user  </li>
<li>satisfies RBAC checks  </li>
<li>satisfies ABAC checks  </li>
</ul>

<p>Instead of following the intended state progression enforced by the client UI, the attacker directly invokes:</p>

<pre><code>POST /checkout/confirm  
</code></pre>

<p>No item has been selected. <br>
No checkout session has been initialized. <br>
No prior server-side state transition has occurred.</p>

<p>From a traditional access-control perspective:</p>

<ul>
<li>authentication passes  </li>
<li>authorization passes  </li>
<li>the endpoint exists and is valid  </li>
</ul>

<p>Unless additional ad-hoc checks exist, the request is processed.</p>

<p>This is not an authentication failure. <br>
This is not an authorization failure.  </p>

<p>It is a <strong>behavioral failure</strong>.</p>

<hr>

<h2 id="19sameattackwithsbac">19. Same Attack With SBAC</h2>

<p>In an SBAC-enabled system, the authoritative interaction graph includes states such as:</p>

<ul>
<li><code>Browsing</code>  </li>
<li><code>CheckoutPending</code>  </li>
<li><code>Complete</code>  </li>
</ul>

<p>The transition <code>ConfirmCheckout</code> is defined as valid only from <code>CheckoutPending</code>.</p>

<p>At the time the stolen token is used, the server’s authoritative state for that session is:</p>

<pre><code>Browsing  
</code></pre>

<p>The computed ghost is:</p>

<pre><code>Gs = { StartCheckout }  
</code></pre>

<p>When the attacker submits <code>ConfirmCheckout</code>, the server evaluates the request against <code>Gs</code>.</p>

<p>The transition is not reachable.</p>

<p>The request is rejected and classified as a <strong>behavioral anomaly</strong>.</p>

<p>As part of anomaly handling:</p>

<ul>
<li>the access token is immediately invalidated  </li>
<li>the active session is terminated  </li>
<li>the legitimate user is required to re-authenticate  </li>
</ul>

<p>This response assumes credential compromise and prioritizes containment over attribution.</p>

<p>This corresponds to the blocked transition path shown in the SBAC diagram, where an out-of-sequence action is rejected before any state mutation occurs.</p>

<h3 id="cexampleanomalyresponserevoketokenforcereauthlockout">C# example: anomaly response (revoke token, force re-auth, lockout)</h3>

<pre><code class="language-csharp">public interface ITokenRevocation  
{
    ValueTask RevokeAsync(string tokenId, CancellationToken ct);
}

public interface IAccountLockout  
{
    ValueTask RegisterAnomalyAsync(string subjectId, SbacAnomaly anomaly, CancellationToken ct);
    ValueTask&lt;bool&gt; ShouldLockoutAsync(string subjectId, CancellationToken ct);
    ValueTask LockoutAsync(string subjectId, TimeSpan duration, CancellationToken ct);
}

public sealed class SbacResponsePolicy  
{
    private readonly ITokenRevocation _revocation;
    private readonly IAccountLockout _lockout;

    public SbacResponsePolicy(ITokenRevocation revocation, IAccountLockout lockout)
    {
        _revocation = revocation;
        _lockout = lockout;
    }

    public async ValueTask HandleAnomalyAsync(ClassifiedClient client, SbacAnomaly anomaly, CancellationToken ct)
    {
        // Always revoke the token used in the anomalous request (credential misuse assumption).
        if (!string.IsNullOrWhiteSpace(client.TokenId))
            await _revocation.RevokeAsync(client.TokenId!, ct);

        // Track anomalies for escalation decisions.
        if (!string.IsNullOrWhiteSpace(client.SubjectId))
        {
            var subject = client.SubjectId!;
            await _lockout.RegisterAnomalyAsync(subject, anomaly, ct);

            if (await _lockout.ShouldLockoutAsync(subject, ct))
                await _lockout.LockoutAsync(subject, duration: TimeSpan.FromMinutes(15), ct);
        }

        // “Force re-auth” is typically implemented by refusing future requests
        // until a fresh token is minted (e.g., 401 + prompt login).
    }
}
</code></pre>

<hr>

<h2 id="20whythismatters">20. Why This Matters</h2>

<p>This attack is realistic and common:</p>

<ul>
<li>tokens are stolen via XSS, malicious extensions, compromised devices, or leaked logs  </li>
<li>the attacker does not need to escalate privileges  </li>
<li>the attacker uses the system exactly as designed, just out of sequence  </li>
</ul>

<p>The request was:</p>

<ul>
<li>authenticated  </li>
<li>authorized  </li>
<li>syntactically valid  </li>
</ul>

<p>It was rejected solely because it was <strong>not correct in the current state</strong>.</p>

<p>SBAC treats this as more than a failed request.</p>

<p>A behavioral anomaly indicates likely credential misuse. As a result:</p>

<ul>
<li>the token is revoked  </li>
<li>the user is prompted to re-authenticate  </li>
<li>the event is logged and correlated  </li>
</ul>

<p>Repeated anomalies originating from the same identity, device, or network context may trigger:</p>

<ul>
<li>progressive trust degradation  </li>
<li>temporary account lockout  </li>
<li>secondary verification requirements  </li>
</ul>

<p>SBAC does not try to prevent token theft. <br>
It assumes credentials will eventually be compromised.</p>

<p>SBAC ensures that compromised credentials cannot be used to violate system behavior silently.</p>

<p>In SBAC, invalid behavior is not only blocked, it is used as a signal to actively protect the user.</p>

<hr>

<h2 id="21relationshiptorbacandabac">21. Relationship to RBAC and ABAC</h2>

<p>RBAC and ABAC still apply.</p>

<p>They determine who may act and under what conditions.</p>

<p>SBAC determines when actions are possible.</p>

<hr>

<h2 id="22limitations">22. Limitations</h2>

<p>SBAC does not prevent server compromise.</p>

<p>It enforces correctness of client interaction, not the purity of execution.</p>

<hr>

<h2 id="23conclusion">23. Conclusion</h2>

<p>State-Based Access Control formalizes a class of security checks that most systems already attempt informally.</p>

<p>By making interaction state explicit, server-authoritative, and enforceable, SBAC closes the gap between “allowed” and “correct.”</p>

<p>By assuming credential compromise and enforcing correctness at the interaction layer, SBAC shifts security from prevention alone to continuous behavioral enforcement.</p>

<p>Clients do not navigate reality. <br>
They propose inputs. <br>
The server decides what is reality.</p>]]></content:encoded></item><item><title><![CDATA[Modeling Subsurface Market Dynamics]]></title><description><![CDATA[<p><img src="https://timothymeadows.com/content/images/2025/10/ocean_50.png" alt=""></p>

<p>⚠️This article is for <strong><em>educational</em></strong> and <strong><em>engineering</em></strong> purposes only. It does not constitute financial advice, investment recommendations, or trading signals. Use the information at your own risk. Currency markets are volatile, and you should do your own research before making any financial decisions.</p>

<p>In the <a href="https://timothymeadows.com/the-tides-model/">first iteration</a> called the tides</p>]]></description><link>https://timothymeadows.com/the-ocean-model/</link><guid isPermaLink="false">c88b6ca7-6871-4f57-851d-6fd560873b35</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Mon, 27 Oct 2025 23:14:58 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://timothymeadows.com/content/images/2025/10/ocean_50.png" alt=""></p>

<p>⚠️This article is for <strong><em>educational</em></strong> and <strong><em>engineering</em></strong> purposes only. It does not constitute financial advice, investment recommendations, or trading signals. Use the information at your own risk. Currency markets are volatile, and you should do your own research before making any financial decisions.</p>

<p>In the <a href="https://timothymeadows.com/the-tides-model/">first iteration</a> called the tides model, we looked at market behavior as surface tides of predictable oscillations captured by <strong>NickRypock Trailing Reverse (NRTR)</strong> and <strong>NickRypock Moving Average (NRMA)</strong> indicators.</p>

<p>They expressed the visible rhythm of market movement, the waves, if you will, that traders "surf" on.</p>

<p>But beneath that surface lie deeper currents!</p>

<p>Liquidity pockets, order flow imbalances, and liquidation clusters act like <strong>thermohaline currents</strong>, quietly steering large-scale behavior long before it’s visible above. <br>
Meanwhile, sentiment events and social cascades act as <strong>storms</strong>, transferring sudden bursts of energy into the market’s atmosphere.</p>

<p>The new model, called the ocean model now introduces three-layers inspired by <strong>fluid dynamics</strong>:</p>

<ul>
<li>Surface Layer (<em>Tides</em>) observable price motion</li>
<li>Subsurface Layer (<em>Underflows</em>) liquidity, order flow, and liquidation dynamics</li>
<li>Atmospheric Layer (<em>Storms</em>) sentiment and emotional energy</li>
</ul>

<p>Together, these layers form a coupled system of forces that can explain how market energy builds, transfers, and releases!</p>

<h4 id="theroaringoceanmodel">The Roaring Ocean Model</h4>

<table>  
  <thead>
    <tr>
      <th>Layer</th>
      <th>Analogy</th>
      <th>Market Mechanism</th>
      <th>Symbol</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Surface</strong></td>
      <td>Waves, tides</td>
      <td>Price action (NRMA/NRTR)</td>
      <td>T<sub>t</sub></td>
    </tr>
    <tr>
      <td><strong>Subsurface</strong></td>
      <td>Deep currents, jets</td>
      <td>Order flow, liquidation heat</td>
      <td>U<sub>t</sub></td>
    </tr>
    <tr>
      <td><strong>Atmosphere</strong></td>
      <td>Storms, wind pressure</td>
      <td>Sentiment dynamics</td>
      <td>&alpha;<sub>t</sub></td>
    </tr>
  </tbody>
</table>

<p>Each layer exchanges energy with the others:</p>

<ul>
<li>Subsurface imbalances <strong>precede</strong> large moves.</li>
<li>Surface waves <strong>translate</strong> deep motion into price.</li>
<li>Sentiment <strong>perturbs</strong> the whole system from above.</li>
</ul>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-5.png" alt=""></p>

<p>This represents the <strong>hydrodynamic energy balance</strong> of the market in one large expressive formula. Now, let's break this down in it's parts, and code!</p>

<h4 id="themarkettidescompositeindex">The Market Tides Composite Index</h4>

<p>At each timestep <sub>t</sub>, given by streaming candles, order data, and sentiment state, the total market energy can be represented as the <strong>Market Tides Composite Index</strong>:</p>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01.png" alt=""></p>

<p><strong>where:</strong></p>

<ul>  
  <li><em>T<sub>t</sub></em> = surface tide strength</li>
  <li><em>U<sub>t</sub></em> = subsurface underflow strength</li>
  <li><em>&alpha;<sub>t</sub></em> = atmospheric sentiment pressure</li>
  <li><em>G<sub>t</sub></em> = nonlinear coupling amplifier</li>
  <li>
    <em>w<sub>T</sub>, w<sub>U</sub>, w<sub>S</sub></em> = layer weights 
    (<em>w<sub>T</sub></em> + <em>w<sub>U</sub></em> + <em>w<sub>S</sub></em> = 1)
  </li>
</ul>

<p>Below is the equivalent C# implementation:  </p>

<pre><code class="language-csharp">public sealed class MtciParams  
{
    public double wT = 0.5, wU = 0.35, wS = 0.15;
    public double wL = 0.6, wShrt = 0.4;
    public double OFP = 0.6, LHF = 0.4;
    public double cT = 1.25, cO = 1.0, cH = 1.0, cS = 1.0;
    public double g1 = 0.4, g2 = 0.6, gMax = 2.0;
    public double Eps = 1e-9; // ε safeguard
}

public sealed class MtciEngine  
{
    private readonly MtciParams _p;
    public MtciEngine(MtciParams p) =&gt; _p = p;

    public double Compute(double price,
                          double nrmaL, double nrtrL,
                          double nrmaS, double nrtrS,
                          double ofp, double lhf,
                          double alpha)
    {
        double T_L = Math.Tanh(_p.cT * ((nrmaL - nrtrL) / Math.Max(price, _p.Eps)));
        double T_S = Math.Tanh(_p.cT * ((nrmaS - nrtrS) / Math.Max(price, _p.Eps)));
        double T = _p.wL * T_L + _p.wShrt * T_S;

        double U = _p.OFP * ofp + _p.LHF * lhf;

        double G = 1.0 + _p.g1 * Math.Abs(ofp) + _p.g2 * Math.Max(0.0, lhf);
        G = Math.Min(G, _p.gMax);

        return _p.wT * G * T + _p.wU * U + _p.wS * alpha;
    }
}
</code></pre>

<h4 id="surfacelayertides">Surface Layer (Tides)</h4>

<p>The <strong>surface layer</strong> measures how NRMA and NRTR diverge the “height” of the market wave across long and short timeframes:</p>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-1.png" alt=""></p>

<p>Below is the equivalent C# implementation:  </p>

<pre><code class="language-csharp">public sealed class NRTR  
{
    public double K { get; }
    private double _trend;
    private double _h, _l, _rev;

    public double Reverse =&gt; _rev;
    public double Trend =&gt; _trend;

    public NRTR(double k, double initial)
    {
        K = k;
        _trend = +1;
        _h = initial;
        _l = initial;
        _rev = initial;
    }

    public double Update(double close)
    {
        if (_trend &gt;= 0)
        {
            if (close &gt; _h) _h = close;
            _rev = _h * (1.0 - K * 0.01);
            if (close &lt;= _rev)
            {
                _trend = -1;
                _l = close;
                _rev = _l * (1.0 + K * 0.01);
            }
        }
        else
        {
            if (close &lt; _l) _l = close;
            _rev = _l * (1.0 + K * 0.01);
            if (close &gt;= _rev)
            {
                _trend = +1;
                _h = close;
                _rev = _h * (1.0 - K * 0.01);
            }
        }
        return _rev;
    }
}

public sealed class NRMA  
{
    public double K { get; }
    public int Fast { get; }
    public int Sharp { get; }
    private NRTR _nrtr;
    private double _prev;
    private readonly Queue&lt;double&gt; _recent = new();

    public double Value =&gt; _prev;

    public NRMA(double k, int fast, int sharp, double initial)
    {
        K = k; Fast = fast; Sharp = sharp;
        _nrtr = new NRTR(k, initial);
        _prev = initial;
    }

    public double Update(double close)
    {
        double r = _nrtr.Update(close);
        double osc = (100.0 * Math.Abs(close - r) / Math.Max(close, 1e-12)) / Math.Max(K, 1e-12);
        _recent.Enqueue(osc);
        while (_recent.Count &gt; Math.Max(3, Fast)) _recent.Dequeue();

        double avg = 0.0;
        foreach (var v in _recent) avg += v;
        avg /= _recent.Count;

        double F = 2.0 / (1.0 + Fast);
        double rho = F * Math.Pow(avg, Sharp);

        _prev = _prev + rho * (close - _prev);
        return _prev;
    }
}
</code></pre>

<h4 id="subsurfacelayerunderflows">Subsurface Layer (Underflows)</h4>

<p>The underflow layer models two invisible but powerful forces:</p>

<p><strong>1.</strong> <strong>Order Flow Pressure</strong> (OFP<sub>t</sub>):
<img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-2.png" alt="">
 </p><p>where <b><em>B<sub>d</sub></em></b> and <b><em>A<sub>d</sub></em></b> are bid/ask volumes within depth <b><em>d</em></b>.</p><p></p>

<p><strong>2.</strong> <strong>Liquidation Heat Flux</strong> (LHF<sub>t</sub>):
<img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-3.png" alt=""></p>

<p>These combine into:</p>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-4.png" alt=""></p>

<p>Below is the equivalent C# implementation:  </p>

<pre><code class="language-csharp">public static class Underflows  
{
    public static double Ofp(double cO, double bidsDepthSum, double asksDepthSum, double eps = 1e-9)
    {
        double num = (bidsDepthSum - asksDepthSum);
        double den = (bidsDepthSum + asksDepthSum + eps);
        return Math.Tanh(cO * (num / den));
    }

    public sealed record LiqEvent(bool IsShort, double Price, double Size, DateTime TimeUtc);

    public static double Lhf(double cH,
                             IEnumerable&lt;LiqEvent&gt; events,
                             double currentPrice,
                             double advVolume,
                             DateTime nowUtc,
                             double lambdaDist = 0.002,
                             double lambdaTime = 0.001,
                             double eps = 1e-9)
    {
        double up = 0.0, down = 0.0;
        foreach (var e in events)
        {
            double dist = Math.Abs(e.Price - currentPrice);
            double dtSec = Math.Max(0.0, (nowUtc - e.TimeUtc).TotalSeconds);
            double w = Math.Exp(-lambdaDist * dist) * Math.Exp(-lambdaTime * dtSec);
            if (e.IsShort &amp;&amp; e.Price &gt; currentPrice) up += e.Size * w;
            if (!e.IsShort &amp;&amp; e.Price &lt; currentPrice) down += e.Size * w;
        }

        double num = (up - down);
        double den = Math.Max(advVolume, eps);
        return Math.Tanh(cH * (num / den));
    }
}
</code></pre>

<h4 id="atmosphericlayerstorms">Atmospheric Layer (Storms)</h4>

<p>The <strong>storm layer</strong> captures exogenous sentiment energy:</p>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-6.png" alt=""></p>

<p>  
  where <em>Z<sub>sss<sub>t</sub></sub></em> is the z-scored sentiment index from social sources (e.g., Twitter).
</p>

<p>Below is the equivalent C# implementation:  </p>

<pre><code class="language-csharp">public sealed class ZScore  
{
    private readonly int _win;
    private readonly Queue&lt;double&gt; _buf = new();
    private double _sum, _sum2;
    private readonly double _eps;

    public ZScore(int window = 300, double eps = 1e-9)
    { _win = window; _eps = eps; }

    public double Update(double x)
    {
        _buf.Enqueue(x);
        _sum += x;
        _sum2 += x * x;
        if (_buf.Count &gt; _win)
        {
            double y = _buf.Dequeue();
            _sum -= y; _sum2 -= y * y;
        }

        int n = _buf.Count;
        double mu = _sum / Math.Max(1, n);
        double var = Math.Max(0.0, _sum2 / Math.Max(1, n) - mu * mu);
        double sigma = Math.Sqrt(var);
        return (x - mu) / Math.Max(_eps, sigma);
    }
}

public static class Storms  
{
    public static double Alpha(double cS, double zScore) =&gt; Math.Tanh(cS * zScore);
}
</code></pre>

<h4 id="couplingamplifier">Coupling Amplifier</h4>

<p>  
  Just as deep currents magnify surface waves, the <strong>coupling term</strong> <em>G<sub>t</sub></em> modulates how underflows amplify tides:
</p>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-7.png" alt=""></p>

<p>Below is the equivalent C# implementation:  </p>

<pre><code class="language-csharp">public static class Coupling  
{
    public static double Gain(double ofp, double lhf, double g1, double g2, double gMax = 2.0)
    {
        double g = 1.0 + g1 * Math.Abs(ofp) + g2 * Math.Max(0.0, lhf);
        return Math.Min(g, gMax);
    }
}
</code></pre>

<h4 id="theroaringocean">The roaring ocean</h4>

<p>Once all layers are computed, we integrate them into an event / loop that runs each market tick (or candle close). This determines whether to enter, exit, or stay out based on the Market Tides Composite Index and the state of each layer!</p>

<p><img src="https://timothymeadows.com/content/images/2025/10/Clipboard01-5.png" alt=""></p>

<p>Below is the equivalent C# implementation:</p>

<pre><code class="language-csharp">public sealed class Logic  
{
    private readonly MtciEngine _engine;
    private readonly double _entry, _exit, _halt;

    public Logic(MtciEngine engine, double entryThreshold = 0.7, double exitThreshold = 0.3, double haltThreshold = 0.85)
    {
        _engine = engine;
        _entry = entryThreshold;
        _exit = exitThreshold;
        _halt = haltThreshold;
    }

    public enum SignalType { None, Long, Short, Exit, Halt }

    public SignalType Evaluate(double price,
                               double nrmaL, double nrtrL,
                               double nrmaS, double nrtrS,
                               double ofp, double lhf,
                               double alpha)
    {
        double mtci = _engine.Compute(price, nrmaL, nrtrL, nrmaS, nrtrS, ofp, lhf, alpha);

        // Storm halt condition — too much sentiment pressure
        if (Math.Abs(alpha) &gt;= _halt)
            return SignalType.Halt;

        bool longTrend = (nrmaL &gt; nrtrL) &amp;&amp; (nrmaS &gt; nrtrS);
        bool shortTrend = (nrmaL &lt; nrtrL) &amp;&amp; (nrmaS &lt; nrtrS);

        if (longTrend &amp;&amp; mtci &gt;= _entry) return SignalType.Long;
        if (shortTrend &amp;&amp; mtci &lt;= -_entry) return SignalType.Short;

        if (Math.Abs(mtci) &lt; _exit) return SignalType.Exit;

        return SignalType.None;
    }
}
</code></pre>

<p><em>Evaluation</em>:</p>

<pre><code class="language-csharp">// Test: called once per candle update
void OnNewCandle(Candle c)  
{
    var nrmaL = nrmaLong.Update(c.Close);
    var nrmaS = nrmaShort.Update(c.Close);
    var nrtrL = nrtrLong.Update(c.Close);
    var nrtrS = nrtrShort.Update(c.Close);

    var ofp = Underflows.Ofp(paramsOFP, totalBids, totalAsks);
    var lhf = Underflows.Lhf(paramsLHF, liquidationEvents, c.Close, advVolume, DateTime.UtcNow);
    var alpha = Storms.Alpha(paramsStorm, sentimentZ);

    var signal = logic.Evaluate(c.Close, nrmaL, nrtrL, nrmaS, nrtrS, ofp, lhf, alpha);

    Console.WriteLine($"{c.Time}: Signal={signal}");
}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Multi-Timescale Trading with NRMA, NRTR, and Sentiment]]></title><description><![CDATA[<p><img src="https://timothymeadows.com/content/images/2025/10/ocean_50.png" alt=""></p>

<p>⚠️This article is for <strong><em>educational</em></strong> and <strong><em>engineering</em></strong> purposes only. It does not constitute financial advice, investment recommendations, or trading signals. Use the information at your own risk. Currency markets are volatile, and you should do your own research before making any financial decisions.</p>

<hr>

<p>Currency markets often feel like standing on</p>]]></description><link>https://timothymeadows.com/the-tides-model/</link><guid isPermaLink="false">8094f0ac-0feb-48de-b38c-207a2c0bc9bb</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Fri, 03 Oct 2025 23:17:32 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://timothymeadows.com/content/images/2025/10/ocean_50.png" alt=""></p>

<p>⚠️This article is for <strong><em>educational</em></strong> and <strong><em>engineering</em></strong> purposes only. It does not constitute financial advice, investment recommendations, or trading signals. Use the information at your own risk. Currency markets are volatile, and you should do your own research before making any financial decisions.</p>

<hr>

<p>Currency markets often feel like standing on a shoreline. Slow tides rise and fall, smaller waves crash on top, and sometimes storms disrupt everything! This is the biases for an idea i had called the tides, or ocean model.</p>

<ul>
<li><p><strong>The Long Tide</strong>: The big swells that lift or lower all boats. In markets, this is the slow trend captured by an adaptive moving average.</p></li>
<li><p><strong>The Short Tide</strong>: The quick in-and-out waves. In markets, these are the short-term reversals we track with a trailing reverse line.</p></li>
<li><p><strong>The Storm Surges</strong>: Sudden shocks from the weather. In crypto, this is Twitter sentiment, where one influential tweet can make or break a trade.</p></li>
</ul>

<p><strong><em>The goal</em></strong>: align trades with both tides, but pause or adjust when a storm blows in!</p>

<p>We’ll break this down into math, C# implementations, and how to combine them across timeframes for assets like ETH, IMX, and GODS.</p>

<h3 id="systemarchitecture">System Architecture</h3>

<p>At a high level, the model has three components:</p>

<ol>
<li><p><strong>Price Indicators (NRMA, NRTR)</strong>  </p>

<ul><li>Run separately on long intervals (daily, weekly) and short intervals (3m, 5m).</li>
<li>Each produces trend and reversal markers.</li></ul></li>
<li><p><strong>Sentiment Layer</strong>  </p>

<ul><li>Tracks influencer Twitter accounts for each coin.</li>
<li>Produces a normalized “storm surge” score.</li></ul></li>
<li><p><strong>Composite Signal Logic</strong>  </p>

<ul><li>Combines long tide, short tide, and sentiment into actionable trades.</li></ul></li>
</ol>

<h3 id="indicatorengineering">Indicator Engineering</h3>

<p><strong>Nick Rypock Trailing Reverse (NRTR)</strong><br>
The short tide is modeled with NRTR, a trailing stop algorithm that flips trend when price crosses a reversal line.</p>

<p><strong>Formulas:</strong></p>

<ul>
<li>In uptrend:<br>
<img src="https://timothymeadows.com/content/images/2025/10/latex.png" alt=""></li>
<li>In downtrend:<br>
<img src="https://timothymeadows.com/content/images/2025/10/latex-1.png" alt=""></li>
</ul>

<p><strong>C# Implementation:</strong></p>

<pre><code class="language-csharp">public class NRTR  
{
    public double K { get; private set; }
    public double Trend { get; private set; }
    public double HPrice { get; private set; }
    public double LPrice { get; private set; }
    public double Reverse { get; private set; }

    public NRTR(double k, double initialPrice)
    {
        K = k;
        Trend = 1; // assume initial uptrend
        HPrice = initialPrice;
        LPrice = initialPrice;
        Reverse = initialPrice;
    }

    public double Calculate(double close)
    {
        if (Trend &gt;= 0)
        {
            if (close &gt; HPrice)
                HPrice = close;

            Reverse = HPrice * (1 - K * 0.01);
            if (close &lt;= Reverse)
            {
                Trend = -1;
                LPrice = close;
                Reverse = LPrice * (1 + K * 0.01);
            }
        }

        if (Trend &lt;= 0)
        {
            if (close &lt; LPrice)
                LPrice = close;

            Reverse = LPrice * (1 + K * 0.01);
            if (close &gt;= Reverse)
            {
                Trend = 1;
                HPrice = close;
                Reverse = HPrice * (1 - K * 0.01);
            }
        }

        return Reverse;
    }
}
</code></pre>

<hr>

<p><strong>Nick Rypock Moving Average (NRMA)</strong><br>
The long tide is modeled with NRMA, an adaptive EMA where volatility is defined by deviations from NRTR.</p>

<p><strong>Formulas:</strong></p>

<p><img src="https://timothymeadows.com/content/images/2025/10/latex-2.png" alt="">
Where:<br> <br>
<img src="https://timothymeadows.com/content/images/2025/10/latex-3.png" alt=""></p>

<p><strong>C# Implementation:</strong></p>

<pre><code class="language-csharp">public class NRMA  
{
    public double K { get; private set; }
    public int Fast { get; private set; }
    public int Sharp { get; private set; }
    public NRTR nrtr { get; private set; }
    public double prevNRMA { get; private set; }

    public NRMA(double k, int fast, int sharp, double initialPrice)
    {
        K = k;
        Fast = fast;
        Sharp = sharp;
        nrtr = new NRTR(k, initialPrice);
        prevNRMA = initialPrice;
    }

    public double Calculate(double close, double[] recentCloses)
    {
        if (recentCloses.Length &lt;= Fast)
        {
            prevNRMA = close;
        }
        else
        {
            double oscSum = 0;
            var count = Math.Min(3, recentCloses.Length);

            for (var i = recentCloses.Length - count; i &lt; recentCloses.Length; i++)
            {
                double deviation = 100 * Math.Abs(recentCloses[i] - nrtr.Calculate(recentCloses[i])) / recentCloses[i];
                oscSum += deviation / K;
            }

            double oscAvg = oscSum / count;
            var NRratio = Math.Pow(oscAvg, Sharp);
            double F = 2.0 / (1 + Fast);

            prevNRMA += NRratio * F * (close - prevNRMA);
        }

        return prevNRMA;
    }
}
</code></pre>

<hr>

<h3 id="sentimentscoringfromtwitter">Sentiment Scoring from Twitter</h3>

<p>Instead of monitoring the entire internet, the model watches curated influencer accounts per coin (e.g., Vitalik for ETH, Immutable devs for IMX, Gods Unchained accounts for GODS).</p>

<p><strong>Pareto Principle in Sentiment Design</strong></p>

<p>Not all voices on Twitter move markets equally. In fact, by the Pareto principle, roughly 20% of accounts generate 80% of meaningful price impact.</p>

<p>Instead of processing millions of tweets, the model only tracks curated influencer accounts for each asset:</p>

<ul>
<li>For ETH, that might mean Ethereum core devs and Vitalik.</li>
<li>For IMX, Immutable team members.</li>
<li>For GODS, the Gods Unchained project leads.</li>
</ul>

<p>This drastically reduces cost and noise, while retaining the majority of predictive power. <br>
The result is a lightweight, event-driven storm surge model instead of a full-blown NLP pipeline.</p>

<p><strong>Formulas:</strong></p>

<p><img src="https://timothymeadows.com/content/images/2025/10/latex-4.png" alt=""></p>

<p><strong>C# Implementation:</strong></p>

<pre><code class="language-csharp">public class TwitterSentiment  
{
    private readonly Dictionary&lt;string, int&gt; _weights;
    private readonly List&lt;(DateTime, int, int)&gt; _events;

    public TwitterSentiment(Dictionary&lt;string, int&gt; influencerWeights)
    {
        _weights = influencerWeights;
        _events = new List&lt;(DateTime, int, int)&gt;();
    }

    public void AddTweet(string account, string text)
    {
        int polarity = ClassifyPolarity(text);
        if (_weights.TryGetValue(account, out int weight))
            _events.Add((DateTime.UtcNow, polarity, weight));
    }

    public double CalculateScore(TimeSpan halfLife)
    {
        double sum = 0;
        foreach (var (timestamp, polarity, weight) in _events)
        {
            double ageHours = (DateTime.UtcNow - timestamp).TotalHours;
            double decay = Math.Pow(0.5, ageHours / halfLife.TotalHours);
            sum += weight * polarity * decay;
        }
        return sum;
    }

    private int ClassifyPolarity(string text)
    {
        string lower = text.ToLower();
        if (lower.Contains("hack") || lower.Contains("exploit") || lower.Contains("lawsuit"))
            return -1;
        if (lower.Contains("launch") || lower.Contains("upgrade") || lower.Contains("partnership"))
            return 1;
        return 0;
    }
}
</code></pre>

<hr>

<h3 id="multitimescaleintegration">Multi-Timescale Integration</h3>

<p>We maintain two indicator sets per asset:</p>

<ul>
<li><strong>Long Tide</strong>: NRMA-long, NRTR-long → daily/weekly candles.</li>
<li><strong>Short Tide</strong>: NRMA-short, NRTR-short → 3m/5m candles.</li>
</ul>

<p>Composite Signal Logic:</p>

<ul>
<li>Long if both long + short are bullish and sentiment ≥ 0.</li>
<li>Short if both long + short are bearish and sentiment ≤ 0.</li>
<li>No trade otherwise.</li>
</ul>

<p>Example (Eth):</p>

<pre><code class="language-csharp">double ethLong = nrmaLong.Calculate(dailyClose, dailyRecentCloses);  
double ethLongRev = nrtrLong.Calculate(dailyClose);

double ethShort = nrmaShort.Calculate(min5Close, min5RecentCloses);  
double ethShortRev = nrtrShort.Calculate(min5Close);

double ethSentiment = twitterEth.CalculateScore(TimeSpan.FromHours(3));

if (ethLong &gt; ethLongRev &amp;&amp; ethShort &gt; ethShortRev &amp;&amp; ethSentiment &gt;= 0)  
    Console.WriteLine("ETH Long");
else if (ethLong &lt; ethLongRev &amp;&amp; ethShort &lt; ethShortRev &amp;&amp; ethSentiment &lt;= 0)  
    Console.WriteLine("ETH Short");
else  
    Console.WriteLine("No Trade");
</code></pre>

<hr>

<h3 id="closing">Closing</h3>

<p>By structuring the market like an ocean system, tides, waves, and storms. We get a model that's both intuitive and implementable.</p>

<ul>
<li>NRTR and NRMA provide the price structure.</li>
<li>Twitter sentiment adds a low-cost shock filter.</li>
<li>Multi-timescale integration ensures coherence.</li>
</ul>

<p>This is not a trading strategy by itself, but a blueprint for engineers to test, refine and learn. There are many variables in the algorithms above which are purposefully not explained. I also did not provide weights for the social scoring algorithm which is likely better replaced with an ai driven sentiment model rather than a simple word database! These are issues left to the reader to solve.</p>

<p>⚠️This article is for <strong><em>educational</em></strong> and <strong><em>engineering</em></strong> purposes only. It does not constitute financial advice, investment recommendations, or trading signals. Use the information at your own risk. Currency markets are volatile, and you should do your own research before making any financial decisions.</p>]]></content:encoded></item><item><title><![CDATA[Gods Unchained - Sealed API]]></title><description><![CDATA[<h1 id="summary">Summary</h1>

<p>On 3/10/2024 the Council of Mortals asked me to audit the sealed portion of the Gods Unchained client to verify if it was actually possible to use cards that where not in your sealed pool, in your sealed deck. This audit was limited in scope to sealed,</p>]]></description><link>https://timothymeadows.com/gu-sealed-audit/</link><guid isPermaLink="false">7eef6343-fda6-4632-92be-c85b2144c215</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Fri, 22 Mar 2024 13:41:12 GMT</pubDate><content:encoded><![CDATA[<h1 id="summary">Summary</h1>

<p>On 3/10/2024 the Council of Mortals asked me to audit the sealed portion of the Gods Unchained client to verify if it was actually possible to use cards that where not in your sealed pool, in your sealed deck. This audit was limited in scope to sealed, and validation that the exploit found only affected sealed.</p>

<p>This post is disclosure of that process. It's important to note that before disclosure. This exploit was fixed on 3/20. It's unknown if the method proposed in this audit was used to fix the exploit. However, testing after the 3/20 patch, i can validate this exploit is no longer working at this time.</p>

<p><img src="https://timothymeadows.com/content/images/2024/03/fixed-discord.png" alt=""></p>

<h6 id="_tldr_"><em>TLDR</em></h6>

<p><strong>Question</strong>: Could you save your sealed deck with cards not in your pool? <br>
<strong>Answer</strong>: Yes, if you know the proto id of the card, or cards you want.</p>

<p><strong>Question</strong>: Can you still do this? <br>
<strong>Answer</strong>: No.</p>

<h4 id="exploit">Exploit</h4>

<p>This exploit could be preformed by capturing and saving requests from inside the client, and then replaying those same requests with the new proto ids you want within the time period it's still valid (about 120 seconds).</p>

<p><img src="https://timothymeadows.com/content/images/2024/03/Screenshot-2024-03-10-at-4.49.56-PM.png" alt=""></p>

<p><em>Note: <a href="https://www.telerik.com/fiddler/fiddler-everywhere">Fiddler Everywhere</a> was used to capture, modify, and replay the traffic for this audit.</em></p>

<p><em>While the scope of this audit was the sealed api, i did take the time to validate if the same injection method was usable in ranked / casual, however, in those modes there is an "ownership" check that prevents this type of injection. Sealed mode does not contain that check so that you can play in sealed with cards you don't own if they are also assigned in your random pool.</em></p>

<h4 id="solution">Solution</h4>

<p>The active sealed sessions API call list's ALL of the cards (proto's) in our random pool. There needs to be a check on the Save Deck API call that double checks that the proto id's (cards) saved in the array are in fact also in the array of proto's (cards) in our pool. If proto's (cards) are contained that DO NOT match, then, AccessDenied should be returned and the deck should not be saved.</p>

<h1 id="details">Details</h1>

<p>Full disclosure this audit was preformed on my personal GU account, no games where played with this modified sealed session. Once proof was gathered, the sealed run was abandoned (RIP 5 gods). This can be verified with the sealed session id in the JSON listed below.</p>

<h4 id="getsealedsession">Get Sealed Session</h4>

<p>This API call will list the active sealed session for a player. It also includes our card_pool which has a list of all proto id's (cards) that we were randomly assigned at the time of buy in.</p>

<p>&#x2192; GET <em>gamemode.prod.prod.godsunchained.com/user/{guid}/gamemode/7/session</em></p>

<ul>
<li>Requires
<ul><li>guid<br>
<em>This is a players id in game. It can be retrieved through various methods.</em></li></ul></li>
</ul>

<p>&#x2190; <em>Returns</em></p>

<pre><code>{"id":"0072e0cb-7c09-4e45-8e27-0726368b2dff","user_id":436860,"buy_in_id":"dd58db39-9cb2-4598-85ec-60fc0c2b85e4","game_mode_global_id":7,"god_pool":["nature","death","magic"],"card_pool":[1132,2002,1605,87061,1159,1680,87056,1215,1605,2325,1604,2328,1260,1165,1130,1154,1673,1616,1032,1293,2003,1245,1295,1122,1248,1294,1122,1011,1532,1751,1083,1122,1292,2263,1182,87069,1243,1245,87067,1698,1564,87049,1570,2257,1168,1668,1753,1045,1092,1043,1092,2313,2257,1676,2311,1081,1101,1551,1081,2311],"deck_id":9864282,"god":"","properties":{"paid":true,"max_wins":7,"max_losses":3,"buy_in_cost":5,"buy_in_currency":"gods"},"win":0,"loss":0,"status":"started","created_at":"2024-03-10T21:01:36.781066Z","updated_at":"2024-03-10T21:49:26.542857Z"}
</code></pre>

<h4 id="savedeck">Save Deck</h4>

<p>This will save a deck with the information contained in the payload. There is no validation made that cards saved are in the pool list above, at the the time of this audit. I did also test trying to use a god you where not assigned at the time of buy in, but this did not work, if you try and use god not assigned you assigned your first god in the list of gods.</p>

<p>&#x2192; <em>PUT deck.prod.prod.godsunchained.com/user/{guid}/deck/{deckid}</em></p>

<ul>
<li>Requires
<ul><li>guid<br>
<em>This is a players id in game. It can be retrieved through various methods.</em>  </li>
<li>deckid<br>
<em>This is the deck id assigned to the current active sealed session. You can get this id from the "Get Sealed Sessions API" response.</em></li></ul></li>
</ul>

<p><strong>Payload</strong></p>

<pre><code>{"id":9864282,"name":"Nature Deck","god":"nature","deck_type":"restricted","ids":[233795109,228897085,78293701,198293435,73982871,209542666,225424635,142817595,221197551,97683802,192815310,64983360,151796170,33781472,226470054,190607562,247505996,193939540,33781468,222887919,233179866,205775320,64892233,64906060,196212367,206068833,168162048,21083152],"protos":[2003,2311],"timestamp":1710104643994,"game_mode_id":7}
</code></pre>

<p>&#x2190; <em>Returns</em></p>

<pre><code>9864282  
</code></pre>

<h4 id="setdeck">Set Deck</h4>

<p>This is used to set the current deck used in sealed. It's mostly a hold over from in the UI from the other modes where you can change decks. You can't actually give a deck id other than the one originally assigned when the session is created.</p>

<p>&#x2192; <em>PUT gamemode.prod.prod.godsunchained.com/user/{guid}/session/{sessionid}/deck</em></p>

<ul>
<li>Requires
<ul><li>guid<br>
<em>This is a players id in game. It can be retrieved through various methods.</em>  </li>
<li>sessionid<br>
<em>This is the session id assigned to the current active sealed session. You can get this id from the "Get Sealed Sessions API" response.</em></li></ul></li>
</ul>

<p><strong>Payload</strong></p>

<pre><code>{
    "deck_id": 9864282
}
</code></pre>

<p>&#x2190; <em>Returns</em></p>

<pre><code>null  
</code></pre>]]></content:encoded></item><item><title><![CDATA[A pillar of strength]]></title><description><![CDATA[<p>In shifting sands, stood a pillar strong and true, <br>
Ninety-seven years of wisdom, in skies of the deepest blue. <br>
She, a beacon of resilience, in every storm, a guiding light, <br>
Her strength, a legacy unyielding, through day and into night.</p>

<p>Her hands, once busy with love's labor, teaching young minds,</p>]]></description><link>https://timothymeadows.com/a-pillar-of-strength/</link><guid isPermaLink="false">e4cede0f-2bd9-403e-af7f-7498f31cb106</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Wed, 28 Feb 2024 21:28:47 GMT</pubDate><content:encoded><![CDATA[<p>In shifting sands, stood a pillar strong and true, <br>
Ninety-seven years of wisdom, in skies of the deepest blue. <br>
She, a beacon of resilience, in every storm, a guiding light, <br>
Her strength, a legacy unyielding, through day and into night.</p>

<p>Her hands, once busy with love's labor, teaching young minds, <br>
In the chill of life's harsh winters, she was that warmth that binds. <br>
A guardian angel, through struggles, she did veer, <br>
In her eyes, a spark of kindness, in her voice, love clear.</p>

<p>Her life, a tapestry of courage, stitched with patience, thread by thread, <br>
In her grandchildren, her spirit echoes, in every step ahead. <br>
Though her presence now is missing, in hearts, she'll always dwell, <br>
For in every act of kindness, her story I continue to tell.</p>]]></content:encoded></item><item><title><![CDATA[R2GU - 1/27]]></title><description><![CDATA[<h1 id="summary">Summary</h1>

<p>Since this is the first post, this update will also contain a short summary of the project. Roughly 2 weeks ago, i decided i wanted to build a robot from Gods Unchained. This is something i used to do for <a href="https://timothymeadows.com/tick-tock-1/">fun</a>, though, previously i had to stop building them</p>]]></description><link>https://timothymeadows.com/r2gu-1-27/</link><guid isPermaLink="false">7ca8c6b7-1d46-4e07-89af-77579c80ec9e</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Sun, 28 Jan 2024 21:25:33 GMT</pubDate><content:encoded><![CDATA[<h1 id="summary">Summary</h1>

<p>Since this is the first post, this update will also contain a short summary of the project. Roughly 2 weeks ago, i decided i wanted to build a robot from Gods Unchained. This is something i used to do for <a href="https://timothymeadows.com/tick-tock-1/">fun</a>, though, previously i had to stop building them due to time constraints.</p>

<p><img src="https://timothymeadows.com/content/images/2024/01/235-removebg-preview.png" alt=""></p>

<p>The original plan was to build a glorified RC car, with a camera, self-balancing, and an android controller. However, with the ability, and expressed interest from outside parties in bidding for the robot. It's overall design evolved from an RC car, to a more "alive" design using a series of AI models to govern it's actions.</p>

<p><img src="https://timothymeadows.com/content/images/2024/01/20240113_120416.jpg" alt=""></p>

<p>The current plan is to use an <a href="https://www.amazon.com/Studio-Jetson-Developer-Computer-Development/dp/B09CPYR31L">Nvidia Jetson Nano</a>, potentially combined with an <a href="https://www.amazon.com/Movidius-Compute-Perfect-Network-Applications/dp/B0BLBKG47B">Intel Neural Compute Stick 2</a> to provide enough computing power to run <a href="https://github.com/TimothyMeadows/Yolo6.NetCore">YOLO</a>, <a href="https://github.com/AmritK10/Urban-Sound-Classification">Urban Sound</a>, and <a href="https://github.com/movidius/ncappzoo/tree/master/apps/gesture_piarm">Gesture</a> models in real time. Will also be adding a speaker, a mic, and some small 6v solar panels on the top of the robot.</p>

<p><img src="https://timothymeadows.com/content/images/2024/01/r2gu.png" alt=""></p>

<h1 id="updates">Updates</h1>

<ol>
<li><p>Jetson nano base image has been updated to support the intel ncs2, was erroring on boot without first unplugging the ncs2. However a hotplug change seemed to resolve this! Now works rebooting without any issues.</p></li>
<li><p>New hardware showed up this week!  </p>

<ul><li><a href="https://www.amazon.com/gp/product/B0CPGPJ3JC">Raspberry PI 5</a><br>
Decided not to use this over the Jetson Nano, mostly due to power draw concerns. We want to keep the power draw as low as we can for battery life, and the PI 5 can draw up to 3 amps more than the Jetson Nano at full load.  </li>
<li><a href="https://www.amazon.com/gp/product/B00KAE2L1C">USB Audio Dongle</a><br>
This is a simple dongle from Amazon by Adafruit. No issues, or quality problems with it.  </li>
<li><a href="https://www.amazon.com/gp/product/B000MYPPPE">Olympus ME-52W Microphone</a><br>
This is a microphone designed for recording audio in a room, it's often used in Olympus audio recorders which are common in corporate, and government environments due to there quality.</li></ul></li>
<li><p>Work has started on 3d modeling the motor casing for the robot. Progress can be seen in the image below.</p></li>
</ol>

<p><img src="https://timothymeadows.com/content/images/2024/01/motor-case-progress.png" alt=""></p>

<h1 id="goals">Goals</h1>

<p>The main goals for next week are to bring the 3d modeling for the motor casing closer to completion. It would be great to have our first test prints by next stream, or the latest being the stream after next. I would also like to get the Jetson Nano image completed by next stream so that testing of the 3d models can begin. Ideally, we will need to monitor memory, and overall resource usage on the jetson with all 3 models running at once. This way if any resource problems arise we can start to plan for them early on!</p>

<p>That's it for this week. Make sure to check out my stream on <a href="https://www.twitch.tv/birdgineer">Twitch</a> every Saturday at 1pm - 5pm, or follow me on <a href="https://twitter.com/birdgineer">Twitter</a> for more updates as the build progresses!</p>]]></content:encoded></item><item><title><![CDATA[Language Understanding, Part 2]]></title><description><![CDATA[<p>In the last <a href="https://timothymeadows.com/language-understanding/">article</a> on language understanding we went over the basic concepts of what language understanding can be, and how this might apply to an intelligent application that accepts free user input.</p>

<p>Microsoft .NET provides a host of ways to train, and consume AI models. This includes both online</p>]]></description><link>https://timothymeadows.com/language-understanding-part-2/</link><guid isPermaLink="false">f233cf04-4aa9-4518-85c7-55e5689f0a89</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Sun, 26 Nov 2023 21:52:31 GMT</pubDate><content:encoded><![CDATA[<p>In the last <a href="https://timothymeadows.com/language-understanding/">article</a> on language understanding we went over the basic concepts of what language understanding can be, and how this might apply to an intelligent application that accepts free user input.</p>

<p>Microsoft .NET provides a host of ways to train, and consume AI models. This includes both online (azure), and offline (ml.net). By default all code samples will be utilizing CPU pipelines for maximum compatibility. However, if you have a modern GPU that supports CUDA you should be able to utilize GPU pipelines as well as CPU pipelines with minimal code changes.</p>

<h4 id="requirements">Requirements</h4>

<p>First we need to create an empty C# .NET 6 or higher project in your editor of choice (mine is often Visual Studio).</p>

<p>Next, we need to install the packages we require, in this tutorial that's only one package. You can install this using nuget console, or just searching out the package by name using manage packages in your ide.</p>

<pre><code class="language-bash">Install-Package Microsoft.ML  
</code></pre>

<h4 id="models">Models</h4>

<p>Not to be confused with AI models, most modern programming paradigms also have a concept known as "models". These models are structures meant to represent how pieces of data look.</p>

<p>When working with AI models you will almost always need a minimum of two models.</p>

<p><img src="https://timothymeadows.com/content/images/2023/11/Untitled-1.png" alt=""></p>

<ol>
<li>A model that represents the structure of our input data.</li>
</ol>

<pre><code class="language-csharp">public class InputData  
{
    [LoadColumn(0)]
    public string Text;

    [LoadColumn(1)]
    public string Label;
}
</code></pre>

<p><em>Note: LoadColumn specifies the order in which we intend the data to be in.</em></p>

<ol>
<li>A model that represents the structure of our output data.</li>
</ol>

<pre><code class="language-csharp">public class OutputData  
{
    [ColumnName("PredictedLabel")]
    public string Prediction { get; set; }
}
</code></pre>

<p><em>Note: ColumnName in our model also represents the column in the output data that we want.</em></p>

<h4 id="training">Training</h4>

<p>Model training requires data that matches our input structure we specified above. Normally this data is supplied through a data set that's often in the form of a spreadsheet, or database. This typically requires sanitizing, or shaping the data before it's use. However, in this article we will just be hard coding a training set of data for the purpose of education.</p>

<pre><code class="language-csharp">var data = new List&lt;InputData&gt;  
{
    new InputData { Text = "Hello", Label = "Greeting" },
    new InputData { Text = "Bye", Label = "Farewell" },
    new InputData { Text = "Can you check order", Label = "OrderStatus" }
};
</code></pre>

<p>Next, we need to load our data into a format that the Microsoft.ML pipeline can consume for training.</p>

<pre><code class="language-csharp">var context = new MLContext();  
var trainingData = context.Data.LoadFromEnumerable(data);  
</code></pre>

<p>Next, we need to fit, and train our model with the data we going to be giving it. Further, because pipelines are designed to be generic. We also need to set various settings letting the pipeline know what we are training, and finally which output columns we should be populating our results.</p>

<pre><code class="language-csharp">var pipeline = context.Transforms.Conversion.MapValueToKey(inputColumnName: "Label", outputColumnName: "Label")  
.Append(mlContext.Transforms.Text.FeaturizeText(inputColumnName: "Text", outputColumnName: "Features"))
.Append(mlContext.MulticlassClassification.Trainers.SdcaMaximumEntropy("Label", "Features"))
.Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel"));

var model = pipeline.Fit(trainingData);  
</code></pre>

<h4 id="consuming">Consuming</h4>

<p>Finally, we can start testing our new model! We can do this by first creating an engine for our training model. In this case, a prediction engine.</p>

<pre><code class="language-csharp">var engine = context.Model.CreatePredictionEngine&lt;InputData, OutputData&gt;(model);  
</code></pre>

<p>Next, we can create a sample input and pass it to our model for classification (prediction).</p>

<pre><code class="language-csharp">var sentence = new InputData { Text = "Can you check order 12345?" };  
var intent = engine.Predict(sentence);

Console.WriteLine($"Text: {sentence.Text}");  
Console.WriteLine($"Intent: {intent.Prediction}");  
</code></pre>

<p>Lastly, we can see that our sentence contains an entity, or in this case an order number. Once we determine that the intent of the sentence to get the status of that order, we can then work on extracting that entity ourselves.</p>

<pre><code class="language-csharp">if (intent.Prediction == "OrderStatus") {  
    var orderIdRegex = new Regex(@"order\s(\d+)");
    var match = orderIdRegex.Match(sentence.Text);
    if (match.Success)
    {
        Console.WriteLine($"Entity: {match.Groups[1].Value}");
    }
}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Language Understanding]]></title><description><![CDATA[<p>Language Understanding in Machine Learning is designed to identify valuable information in text such as user goals (intents) and key information from sentences (entities). These are often mapped (events) to methods that can preform more complex work on behalf of the intention.</p>

<p>This is a basic explanation of how this</p>]]></description><link>https://timothymeadows.com/language-understanding/</link><guid isPermaLink="false">f8255cbc-3244-4fe7-a904-3fd6b7ddf56d</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Thu, 23 Nov 2023 23:07:29 GMT</pubDate><content:encoded><![CDATA[<p>Language Understanding in Machine Learning is designed to identify valuable information in text such as user goals (intents) and key information from sentences (entities). These are often mapped (events) to methods that can preform more complex work on behalf of the intention.</p>

<p>This is a basic explanation of how this might work in an intelligent application.</p>

<h4 id="intent">Intent</h4>

<p>When you receive text you need to parse the biggest question will always be, "<em>What is the intent of this message?</em>". This can be further compounded when multiple intents are expressed in the same message as, "<em>What are all of the intents of this message?</em>". Especially when speaking through speech to text were we often try and cram as much information as we can in a short period of time.</p>

<p><img src="https://timothymeadows.com/content/images/2020/02/message-block-2-1.png" alt=""></p>

<p>The illustration above shows extracting multiple intents from a single message. The first intent is a greeting. This indicates a polite person with a positive mood, it might mean returning a greeting in response. The second intent is the actual purpose of the message. In this case someone is requesting a status update for an order.</p>

<h4 id="entities">Entities</h4>

<p>Once you have established intent for a message. You might find that you need to extract more detailed information that should otherwise exist. This might be an email, url, social handle, number etc.. These bits of information only have a clear meaning when attached to there supposed intent.</p>

<p><img src="https://timothymeadows.com/content/images/2020/02/message-block-1-3.png" alt=""></p>

<p>This illustration shows that the message has given us a "<em>greeting</em>" and is looking for an "<em>order status</em>" update. This establishes the intent of our message. The second intent, which is also the primary purpose of the message, and contains 2 parts of information we might need.</p>

<ol>
<li><p>The first is a number which because we know the intent is to get a status update for an order, this number should be our order id.</p></li>
<li><p>The second part of the information is the location of that order. In this case the message is saying on a website we should know about.</p></li>
</ol>

<h4 id="events">Events</h4>

<p>Mapping a series of intent's to an event is a common way of dealing with "<em>What to do</em>" once you have extracted the intention of a message with it's corresponding entities. This can be done by tagging each intent with an easy to understand name which also represents the method(s) that should trigger when the matching intent is found.</p>

<p>The first intention we have found can be labeled as a "<em>Greeting</em>". This can be mapped to an "<em>OnGreet</em>" event which selects a proper greeting based on the current time of day. Such as, "<em>Good morning</em>", "<em>Good afternoon</em>", or "<em>Good evening</em>".</p>

<p>The second intention we have found can be labeled as "<em>OrderStatus</em>". This can be mapped to an "<em>OnOrderStatus</em>" event which then checks the matching API, using the extracted entities found in the intent. Then constructing a response with the correct order status information, or an error if the order, or status can't be found.</p>]]></content:encoded></item><item><title><![CDATA[How To Install Gods Unchained on the Steam Deck]]></title><description><![CDATA[<p><img src="https://timothymeadows.com/content/images/2022/07/FXVq2g3VEAA7GT52.jpg" alt=""></p>

<h3 id="steamdeck">Steam Deck</h3>

<p>The steam deck is a powerful, by default Linux based handheld gaming system released by Valve. The price (at the time of writing) can range from $399 to $649. There is also likely a wait time before shipment as there just being released.</p>

<p>To me, the most powerful</p>]]></description><link>https://timothymeadows.com/how-to-install-gods-unchained-on-the-steam-deck/</link><guid isPermaLink="false">cc444cda-d1a3-4241-bf51-d33b509bf841</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Tue, 12 Jul 2022 03:51:41 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://timothymeadows.com/content/images/2022/07/FXVq2g3VEAA7GT52.jpg" alt=""></p>

<h3 id="steamdeck">Steam Deck</h3>

<p>The steam deck is a powerful, by default Linux based handheld gaming system released by Valve. The price (at the time of writing) can range from $399 to $649. There is also likely a wait time before shipment as there just being released.</p>

<p>To me, the most powerful thing about the steam deck, is the fact that SteamOS is actually just Archlinux with Valve/Steam's logo on it. More importantly we have full root access to OS out of the box! Bravo Valve!</p>

<h3 id="godsunchained">Gods Unchained</h3>

<p>Gods Unchained is a blockchain based trading card game from Immutable. Currently, you can only play Gods Unchained on a PC, Mac, or Linux (unofficial support). You can read more about the game <a href="https://godsunchained.com/">here</a>. I highly recommend you check it out if you like trading card games!</p>

<p>Below are the steps required to install gods unchained on the steam deck using steam, and proton 7. Please note that while this "can" be done using the virtual keyboard in desktop mode. This method WILL NOT be covered here. Instead, the steam deck charge power also doubles as a USB-C hub. It's assumed that you have connected a mouse + keyboard.</p>

<h3 id="desktopmode">Desktop Mode</h3>

<p>The steam deck has 2 modes. Desktop Mode, and Game Mode. By default the steam deck will boot in Game Mode. In order to enter Desktop Mode you need to:</p>

<ol>
<li>While running in Game Mode. Hold down the power button for 2 - 3 seconds. This will open a menu on the steam deck.  </li>
<li>Navigate to Switch to Desktop. This will cause the steam deck to reboot into a windows looking desktop.</li>
</ol>

<p>On the desktop you will see an icon labeled "Return to Game Mode". You can click on this to return to previous mode you where in. If you have not yet connected a keyboard to the USB-C power port using a USB-C hub, you should do so now.</p>

<h3 id="downloadimmutable">Download Immutable</h3>

<p>By default the steam deck comes with Firefox installed. You can also install Chrome. Ideally you can install which ever browser you plan to use Metamask, or another browser extension with in order to access the Immutable X market place. Both browsers can equally be added as a "non-steam" application for use in Game Mode.</p>

<ol>
<li>Open a web browser, and navigate to <a href="https://godsunchained.com/">godsunchained.com</a>.  </li>
<li>Click the Play Gods Unchained button, and download the Immutable exe to your downloads folder.  </li>
<li>You may wish to search for "gods unchained logo" using an image search in the browser of your choice while you download. You may need it, and a background image later for steam launcher.</li>
</ol>

<h3 id="installimmutable">Install Immutable</h3>

<p>You can install immutable, and most applications in steam as non-steam games. This is a two step process that requires first adding the installer, then, removing it. Then adding the exe that was just installed onto your steam deck with the installer as it's own non-steam game.</p>

<ol>
<li>Open Steam, it's icon should be located on the desktop next to the Return to Game Mode icon.  </li>
<li>In the bottom left of the steam interface, click the Add Game button. Scroll to the Add Non-Stream Game option in the menu.  </li>
<li>Click Browse in the Add Game interface. Then navigate to the Immutable exe in your downloads folder located at <code>/home/deck/Downloads</code>.  </li>
<li>Click Add Selected Program at the bottom of the Add Game interface.  </li>
<li>Navigate to the steam library in steam. Library -> Home and search for "Immutable" you should see the immutable exe you added before.  </li>
<li>Right click the exe in the library and select Properties.  </li>
<li>Click the Compatibility tab on the left hand side.  </li>
<li>Check off "Force the use of" option.  </li>
<li>Select Proton 7.0-N where N is the larger number in the list for 7.0. Other versions may work but this is the only version I have tested.  </li>
<li>Close the Properties window using the X in the upper right. Then click the Play button for the Immutable exe in your steam library.  </li>
<li>Follow the install steps, do not change any paths during install, just use the defaults provided by the installer.  </li>
<li>Once the installer completes and your at the login screen. Close the Gods Unchained application clicking the X in the upper right corner. While you shouldn't have any issues playing from here, it's unlikely to save your login information while still using the installer thread.  </li>
<li>Right click on the Immutable installer in the steam library and select the remove non-steam game option. You don't need the installer anymore.</li>
</ol>

<p>Note: Your screen may flicker, or flash while the Gods Unchained login screen is loading  the Palis animation for the first time. It's unknown if this is Proton, or the steam deck causing this, however, if you just wait, it will self correct. This is also true for the first run in Game Mode.</p>

<h3 id="addgodsunchained">Add Gods Unchained</h3>

<p>Now that gods unchained is installed! We need to tell steam where it's located. We can then tell steam which version of proton to use, optionally which icon, and background image to use, and then finally, play!</p>

<ol>
<li>If you closed steam before. You will need to reopen it. It's icon should be located on the desktop next to the Return to Game Mode icon.  </li>
<li>In the bottom left of the steam interface, click the Add Game button. Scroll to the Add Non-Stream Game option in the menu.  </li>
<li>Click Browse in the Add Game interface. Then navigate to the Immutable launcher exe in your steam folder located at: <code>/home/deck/.local/share/Steam/steamapps/compatdata/4117854237/pfx/drive_c/users/steamuser/AppData/Local/Programs/immutable-launcher/immutable.exe</code>. <br>
<em>Note: The numbers in the path above are randomly created at the time install. They won't be the same as what's above. You can sort the compatdata folder by date/time to help locate which numbered folder contains the launcher you installed.</em></li>
<li>Click Add Selected Program at the bottom of the Add Game interface.  </li>
<li>Navigate to the steam library in steam. Library -> Home and search for "Immutable" you should see the immutable launcher exe you added before.  </li>
<li>Right click the exe in the library and select Properties.  </li>
<li>Click the Compatibility tab on the left hand side.  </li>
<li>Check off "Force the use of" option.  </li>
<li>Select Proton 7.0-N where N is the larger number in the list for 7.0. Other versions may work but this is the only major version I have tested.  </li>
<li>If you downloaded an icon before you now navigate to the Shortcut Tab and change the game icon. You may wish to do this even if you did not download a logo as you can also change the Shortcut name from Immutable to "Gods Unchained".  </li>
<li>Close the Properties window using the X in the upper right.  </li>
<li>If you downloaded a background image. Right click on the grey space in the library with the Gods Unchained (or Immutable if you did not rename it) application and select "Set Background Image".  </li>
<li>Exit steam in desktop mode. Then click the Return to Game Mode icon. While you can play in Desktop Mode, ideally, you do not want too.</li>
</ol>

<p>You should now see Gods Unchained (or Immutable if did not rename) in your Stream Library, and your Recent Games list in Games Mode. Steam will not record time played in this mode :( but you can still use the steam overlay, and chat with friends while in game.</p>

<p>You can use the touch screen to play! </p>

<p>There are some minor issues:</p>

<ol>
<li>50% of the time holding down your finger on a card in your hand, or on the board will show the popup showing what that card is, and 50% of the time it will do nothing at all.  </li>
<li>The options, and report bug buttons at the upper right are very hard to click with your finger.  </li>
<li>Mana pips can some times take a few taps to trigger due to there small button size. All, in all however, the game is extremely playable with little to no lag with High settings.</li>
</ol>]]></content:encoded></item><item><title><![CDATA[Facebook, and our future]]></title><description><![CDATA[<p>I do not dislike social media as a whole. However, I do not like, and can no longer support how facebook abuses extremely powerful AI modeling formulas to locate, and market to easy to manipulate people.</p>

<p>Facebook is a veritable giant in the AI modeling world. It has trained models</p>]]></description><link>https://timothymeadows.com/facebook-and-our-future/</link><guid isPermaLink="false">4e068bf0-c8f4-4376-b0eb-c4a9181f1c0a</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Wed, 13 Oct 2021 23:30:34 GMT</pubDate><content:encoded><![CDATA[<p>I do not dislike social media as a whole. However, I do not like, and can no longer support how facebook abuses extremely powerful AI modeling formulas to locate, and market to easy to manipulate people.</p>

<p>Facebook is a veritable giant in the AI modeling world. It has trained models to locate fringe, angry, and emotionally fragile people. Then, rather than remove, or help those people. They have decided it was best to experiment with them. To see how far they could manipulate these people into following, and interacting with content that they curated just for them. Further, they experimented with how long they could entice users to remain engaged with content of the same type. The content they where curating was not harmless. It was specifically curated to target emotions that AI models had detected in the previous groups of people that where easiest to stimulate into interacting with.</p>

<p>Basically, through extreme research, facebook found out you can get people who are on the fringe edges of society to click on content that validates, or re-enforces there world view. They, also, found out you can manipulate those same people into repeatedly click on content that continues to validate there views more aggressively if there are fewer sources outside of facebook that they can find the same level of re-assurance that they are not alone. That means, crazier the content. The more likely it was only on facebook, and that meant the people looking for re-enforcement from that type of content, would remain on facebook longer. The more people they could target, the more data they built up, and in turn, the more accurate the there ability to create curated content streams that can be interacted with for longer, and longer.</p>

<p>Regardless of why facebook was created, or what it offers us today. The main reason facebook now exists is to build a massive empire of data. The reason facebook is one of the worlds largest contributors to the artificial intelligence modeling in the world is because they needed advanced enough methods to harvest that much data. They want to target people with high enough accuracy that advertisers will pay them top dollar to have there ads placed with the highest chance for interaction. Regardless of what that interaction is! In the end, facebook is nothing more than a system for stimulating hate, fear, and depression because it's easy to make money.</p>

<p>Social media as a concept is not wrong. Companies, like facebook, who then use that information to target the easiest to manipulate among us, for profit! IS. NOT. RIGHT.</p>]]></content:encoded></item><item><title><![CDATA[Tick, Tock - Part 1]]></title><description><![CDATA[<p>I saw a clock work bird named a Tocker in a popular video game called Team Fight Tactics. Though unrealistic of a design being in a fantasy world. More to point, a video game. The more i played, the more I realized the simplistic concept he started with, which was</p>]]></description><link>https://timothymeadows.com/tick-tock-1/</link><guid isPermaLink="false">72885fd6-957a-4af5-b3aa-4eeb5c3fa796</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Sun, 07 Mar 2021 17:14:01 GMT</pubDate><content:encoded><![CDATA[<p>I saw a clock work bird named a Tocker in a popular video game called Team Fight Tactics. Though unrealistic of a design being in a fantasy world. More to point, a video game. The more i played, the more I realized the simplistic concept he started with, which was a ball with a beak and eyes was something i could build.</p>

<p><img src="https://timothymeadows.com/content/images/2021/03/tock-1.png" alt=""></p>

<h1 id="modelingtheball">Modeling the ball</h1>

<p>My tool of choice for modeling is, well, my web browser. I have found that <a href="https://www.tinkercad.com/">Tinkercad</a> has been one of the least complicated modeling tools i have been able to learn. Modeling is not my day job, nor should i ever expect it to be! There is some serious limits with what Tinkercad can do in relation to SketchUp, or similar much more complex tools. However, this works for me.</p>

<p>I started with taking a 3d sphere in tinker cad, and expanding to a reasonable size. In this case, reasonable means i could at least fit a single Raspberry PI zero, and a battery with some left over space for servos and wires. To simulate this i created a square box with the height and width to fit all of the above hardware. I then expanded the sphere to contain the box in the center. This was enough that any future size needed could be obtained though scaling the model.</p>

<p>I had to hollow out the sphere while leaving enough of a wall not to sacrifice too much integrity. Followed by cutting the sphere in half for easy access to it's inside. The original Tocker did have what appeared to be a horizontal cut up to his face in which case the cut line simply vanishes. Obviously i needed a full cut line, and it was going to be a problem horizontal if i also wanted solid eye sockets. So i decided i needed to go with a vertical cut.</p>

<p><img src="https://timothymeadows.com/content/images/2021/03/tock-2.png" alt=""></p>

<p>Deciding how re-attach the ball was going to be a challenge. Normally i depend on a typical dowel rod style attachment setup in my designs. While effective, it also tends to help hold the shape of objects that are tossed around. However, being i was likely to need constant access to the inside of the ball, i decided that dowels would most likely break. </p>

<p>Steam punk style braces where considered, and honestly i might still add them anyway because they look cool. However, ultimately i went with a screw design. Being able to screw, and unscrew the ball felt like the perfect medium for what i was looking for. After searching Thingiverse before trying to embed the nut, and bolt myself into the ball. I managed to find <a href="https://www.thingiverse.com/thing:61205">this</a>. After modifications i was able to expand the screw ball to match the sphere i had just modeled exactly. Through some test prints, the ball was able to screw, and unscrew perfectly (provided it was printed without supports near the screw grooves).</p>

<h1 id="modelingthefeatures">Modeling the features</h1>

<p>Features, consist of 4 separate models. Once they where each created they could be merged, or wrapped with the ball.</p>

<h3 id="theeyesockets">The eye sockets</h3>

<p><img src="https://timothymeadows.com/content/images/2021/03/tock-3.png" alt=""></p>

<p>The eyes from my perspective where simplistic. The previous ball i had created before finding the the screw ball was resized, duplicated and hollowed out a second time to match the size of the camera lenses i was planning to use. The incredibly difficult part was calculating and then measuring to the exacting placement with the slight offsets for each eye to provide a clean widescreen merged image, in addition to being used as separate sources.</p>

<h3 id="thefaceplate">The face plate</h3>

<p><img src="https://timothymeadows.com/content/images/2021/03/tock-4.png" alt=""></p>

<p>The face plate was one of the more difficult things for me to model. Like I've stated before modeling is not my job, and i am not a professional. It took me some time to wrap my head around the shapes i needed to combine to create the face plate. More difficult was how to actually wrap the face plate around a circular surface area.</p>

<h3 id="theantenna">The antenna</h3>

<p><img src="https://timothymeadows.com/content/images/2021/03/tock-5.png" alt=""></p>

<p>The antenna was pretty easy after having completed the other two models already which where far more challenging and educational. More or less the antenna is just 4 triangles formed into 2 diamonds all merged together. I then duplicated it and used the second to hollow out the first so that a small antenna, and circuit board, and 2 wire can fit inside.</p>

<h3 id="thebeak">The beak</h3>

<p><img src="https://timothymeadows.com/content/images/2021/03/tock-6.png" alt=""></p>

<p>I had tried a few variations on the beak. The first was using half of the diamond from the antenna as a beak triangle. This worked well enough, but since i planed to add a speaker for audio. It made more sense to open the mouth. So i cut the triangle in half, and carefully split it until it formed an open looking mouth, with, enough of a hole for audio to pass though from a speaker behind it.</p>

<h1 id="whatsnext">What's next</h1>

<p>While part of the goal of this project has been to bring to life a Tocker from Team Fight Tactics. The ultimate goal has been to create a telepressence unit that can travel with me. Providing a birds eye shoulder view of what i see and do. Automatically classifying objects around it, able to direct, and follow it's own curiosities. While also providing a pleasing interface for on looking humans to interact with the environment. Stay tuned for part 2!</p>]]></content:encoded></item><item><title><![CDATA[The Pi-Top AI]]></title><description><![CDATA[<p>I recently went to purchase a new <a href="https://pi-top.com/">pi-top</a> for this project. However i was disheartened to find the original pi-top laptop was no longer sold on the pi-top website. While i have purchased the current model being sold. I found it to be much to limited in comparison to the</p>]]></description><link>https://timothymeadows.com/the-pi-top-ai/</link><guid isPermaLink="false">2e0daa59-098d-41dd-b117-837ee11ae360</guid><dc:creator><![CDATA[Timothy D Meadows II]]></dc:creator><pubDate>Wed, 08 Jan 2020 02:32:27 GMT</pubDate><content:encoded><![CDATA[<p>I recently went to purchase a new <a href="https://pi-top.com/">pi-top</a> for this project. However i was disheartened to find the original pi-top laptop was no longer sold on the pi-top website. While i have purchased the current model being sold. I found it to be much to limited in comparison to the original model. In an attempt to be more specific, i simply can't tinker or build onto the current model with different types of raspberry pi compatible boards like i can with the original. Thankfully i already had an original pi-top with which to re-use.</p>

<p><em>If this is being read by anyone on the pi-top team. Then please consider reselling, or at the very least creating a newer more compatible pi-top for tinkers. I would love to continue pushing what they can do. Thank you for your work non the less.</em></p>

<p>A few years ago I had made a post about the pi-top and a child-hood <a href="https://www.timothymeadows.com/the-pi-top/">dream</a> I had of creating a modular PC for developers. With the joy, and success I had creating the previous pi-top. I decided to build another, this time creating in the form of a cluster of pi's contained inside the pi-top known as the pi-top <a href="https://www.timothymeadows.com/the-pi-top-cluster/">cluster</a>. This time around i wanted to expand on what the cluster could do by upgrading it with a new raspberry pi 4 and adding hardware specifically to help with offloading execution of tensorflow, and yolo models in real time with an IR camera.</p>

<p><img src="https://timothymeadows.com/content/images/2020/01/81911053_2754319771295224_4226735396836343808_n.jpg" alt=""></p>

<h1 id="theidea">The Idea</h1>

<p>My day, to day job(s), consist of programming in JavaScript, and C# as well as managing a large farm of Servers, Web containers, Redis nodes, SQL servers, Database containers, Elastic pools and Storage pools in Microsoft Azure, Amazon Web Services, and physical on-site servers.</p>

<p>Most of the side jobs i find beyond the above work tend to revolve around AI work in tensorflow, and caffe such as simplified predictors for logging, load balancing in more rare cases price prediction. With more advanced methods for image classification, object detection, brand detection, speech to text, and text to speech.</p>

<p>The goal this build was to create a system that could both train, and execute most of the models that i work with in relation to face detection, face identification, object detection, image classification, speech to text, and text to speech. The Neural Compute Stick 2 from Intel can do most of this on it's own though does depend on the system it's attached.</p>

<p>While the controller (pi4) is being used for Camera input. Ultimately the idea will be to remove the camera on the controller, and instead use 2 camera's each on it's own pi zero w creating a left, and right eye for robotics work. One eye would ideally be an IR camera while the other would be a more traditional camera. These eyes will stream back to the controller over wifi using ffmpeg, rtms, and nginx.</p>

<h1 id="hardware">Hardware</h1>

<ul>
<li>Pi-Top Original Laptop Shell (not sold anymore?)</li>
<li><a href="https://shop.pi-top.com/collections/frontpage/products/pi-topproto">Pi-Top Proto</a></li>
<li><a href="https://www.amazon.com/gp/product/B07TC2BK1X/">Raspberry PI 4</a> (4 GB)</li>
<li><a href="https://clusterhat.com/">ClusterHat</a></li>
<li>4x <a href="https://www.amazon.com/Raspberry-Pi-Zero-Wireless-model/dp/B06XFZC3BX/">Raspberry PI Zero W</a></li>
<li><a href="https://www.amazon.com/gp/product/B07KT6361R/">Neural Compute Stick 2</a></li>
<li><a href="https://www.amazon.com/kuman-Raspberry-Camera-Module-Supports/dp/B0759GYR51/">IR Camera</a> (720p - 60 FPS)</li>
<li>5x <a href="https://www.amazon.com/Samsung-Class-Adapter-MB-MC32GA-AM/dp/B0749KG1JK/">SD Card</a> (Class 10 - 32 GB)</li>
<li><a href="https://www.amazon.com/gp/product/B07W3MJ9Y2/">Raspberry Pi 4 Model B Fan</a></li>
<li><a href="https://www.amazon.com/gp/product/B00V5KRF66/">Micro HDMI Adapter</a> (Type-A F - Type-D M)</li>
<li><a href="https://www.amazon.com/gp/product/B07CBW12Z6/">USB Type C Cable</a> (1ft)</li>
<li>2x <a href="https://www.amazon.com/SanDisk-64GB-Cruzer-Flash-Drive/dp/B07MDXBTL1/">USB Disk Drive</a> (64 GB)</li>
<li><a href="https://www.amazon.com/gp/product/B00L2442H0/">USB 2.0 Hub</a> (4 Port)</li>
<li><a href="https://www.amazon.com/gp/product/B00CJG2ZYM/">USB 2.0 Extension Cable</a> (1ft)</li>
<li><a href="https://www.amazon.com/gp/product/B00KAE2L1C/">USB Audio Adapter</a></li>
<li><a href="https://www.amazon.com/gp/product/B000MYPPPE/">Noise Canceling Microphone</a></li>
</ul>

<h1 id="design">Design</h1>

<p>The design follows much of the original design issues i had when creating the cluster version. You can read more about the issues faced in that build <a href="https://timothymeadows.com/the-pi-top-cluster/">here</a>. Similar to the previous build. The primary issue with this upgrade was space. Fitting everything together while also allowing the pi-top lid to slide close was a tall order... One i was not able to meet this time unlike the previous build. This turned out however to be for the best since the second issue this upgrade faced was distribution of heat in the case. Had the lid been left closed for too long devices would have started to overheat. Finally, there was also the change to USB Type-C for power, and Micro HDMI for video in the Raspberry PI 4.</p>

<p><img src="https://timothymeadows.com/content/images/2020/01/81996695_309801996611828_8241367591856111616_n.jpg" alt=""></p>

<p>I will be creating a future blog detailing the software being used here, a long with issues that occurred getting everything to work together. This blog will also include source code used to create a distributed ai system over ip as well as the 2 eye system explained above in the idea section. Stay tuned to this blog for more information if this sounds interesting to you.</p>

<p>Thank you for reading!</p>

<p><em>Update: The Pi-Top has reached out to me and provided me with a free v1 to continue future projects with. Thanks Pi-Top team!! &lt;3. I will be considering what to do with the new v1... perhaps something more solar driven this time!</em></p>]]></content:encoded></item></channel></rss>