<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: San</title>
    <description>The latest articles on DEV Community by San (@san-tekart).</description>
    <link>https://dev.to/san-tekart</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2600838%2Fdd9574c6-50ec-4901-8bca-d283e4de3bbd.png</url>
      <title>DEV Community: San</title>
      <link>https://dev.to/san-tekart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/san-tekart"/>
    <language>en</language>
    <item>
      <title>My specs never told me what to do next, so I built my own SDD harness</title>
      <dc:creator>San</dc:creator>
      <pubDate>Sun, 11 Oct 2026 06:43:11 +0000</pubDate>
      <link>https://dev.to/san-tekart/my-specs-never-told-me-what-to-do-next-so-i-added-a-goal-document-next-to-them-3l7g</link>
      <guid>https://dev.to/san-tekart/my-specs-never-told-me-what-to-do-next-so-i-added-a-goal-document-next-to-them-3l7g</guid>
      <description>&lt;p&gt;I do spec-driven development with an AI agent. I tried some well-known spec-driven harnesses, then built my own tool, and for months now I have used only that. This post explains why I needed my own tool and when it is useful.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/opellen/opengoal" rel="noopener noreferrer"&gt;https://github.com/opellen/opengoal&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The question my specs did not answer
&lt;/h2&gt;

&lt;p&gt;Like many developers who got hooked on AI coding, I was working on several projects at the same time, like crazy. At some point I felt the need for spec-driven development, so I used the well-known harness frameworks. But I ran into a bottleneck:&lt;/p&gt;

&lt;p&gt;"What do I do next?"&lt;/p&gt;

&lt;p&gt;The specs did not tell me what to do next.&lt;/p&gt;

&lt;p&gt;As a workaround, I made a page for each project in Notion and managed things there. But it was a separate place from the project, so keeping it up to date was a hassle. So I wanted state management that lives inside the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built my own
&lt;/h2&gt;

&lt;p&gt;My SDD harness started in a hobby project: reverse engineering a game. Reverse engineering takes a lot of analysis and design over many sessions, and designs and plans get overturned all the time. Each time, brainstorming with the AI harness again and rewriting the spec was too much work. If that process runs through a CLI, it is even less agile. I thought about editing the prompts of the existing harnesses, but then I could not keep up with their updates. So I also wanted the prompts of my own harness to be extendable, in a way similar to Jekyll.&lt;/p&gt;

&lt;p&gt;There were a few more reasons.&lt;/p&gt;

&lt;p&gt;The AI sometimes ignored the later part of a slash command in a harness. I learned that the cause was long text written in a prose-like natural language style. So I started to optimize the prompts. I based this on research showing that pseudocode-style prompts are followed better than natural language and use fewer tokens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EMNLP 2023: pseudocode-style prompts improved F1 scores by 7 to 16 points over natural language.&lt;/li&gt;
&lt;li&gt;CodeAgents (2025): writing agent workflows in pseudocode reduced token use by 55 to 87% and improved performance by 3 to 36 points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Forced strict TDD was another reason. Small tasks got the same strict TDD as big ones, and if errors still remained, I had to run the process again. The tokens burned for nothing that way were painful.&lt;/p&gt;

&lt;h2&gt;
  
  
  One question, one file
&lt;/h2&gt;

&lt;p&gt;My harness is based on one rule: one question, one file. If you want to ask the project something, the answer is in exactly one file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROADMAP.md  ─────────────  where am I in the big picture?
     CONTEXT.md  ────────  what is this project?
            ● GOAL.md  ──  what do I do now?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure is kept in the docs folder by default. You can also choose another location.&lt;/p&gt;

&lt;p&gt;Some harness frameworks have a similar structure. But my harness, OpenGoal, does not use a CLI or hooks. It is plain markdown. Nothing extra is added at runtime, so it runs light. I mostly use it in Claude Code, but it can be installed into about 20 tools, including Cursor and Codex.&lt;/p&gt;

&lt;p&gt;Spec-driven harness frameworks expect a carefully structured spec, but that can be too much for small tasks. So OpenGoal can create just a goal. You can split that goal into smaller pieces as far as you need, and for simple work you can skip the design document. I did not throw specs away. I write them to fit the size of the task, and GOAL.md is the spec that answers "what do I do next?"&lt;/p&gt;

&lt;h2&gt;
  
  
  How I use it
&lt;/h2&gt;

&lt;p&gt;When OpenGoal is not set up in a project yet, or when I want to discuss something with the AI, I start with scout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: /opgl:scout
AI:  Found an existing codebase. No CONTEXT.md yet.
     Set up project context with `/opgl:context`?

You: /opgl:context init
AI:  ✓ docs/CONTEXT.md created
     To set a goal: `/opgl:goal init`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the discussion has given enough context about the goal, I ask it to write the goal like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: /opgl:goal init Migrate auth module from JWT to session-based
AI:  ✓ docs/GOAL.md created (5 tasks)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the AI may recommend breaking the goal down further, writing a design document, or, for simple work, starting right away without a design document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI:  Recommend `/opgl:goal breakdown` — the tasks contain several hidden steps.
AI:  Recommend `/opgl:design init` — implementation task with file-level decisions.
AI:  Recommend `/opgl:go` — this is a simple task.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the work needs a design, the design document comes first. In OpenGoal, this document works as the spec for a unit of work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: /opgl:design init
AI:  ✓ docs/DESIGN.md created
     Session store selection, migration strategy, rollback plan included
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the work, when there is an important decision, it asks for my opinion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: /opgl:go
AI:  Task 1/5: Set up session store... ✓ done
     Task 2/5: Replace middleware... ✓ done
     Task 3/5 requires DB schema changes. Proceed?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you want to handle longer-term goals, you can write a roadmap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: /opgl:roadmap init
AI:  ✓ docs/ROADMAP.md created
     ├── M1: Auth migration
     ├── M2: Rate limiting
     └── M3: API versioning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finishing and pausing a goal
&lt;/h2&gt;

&lt;p&gt;When all the tasks of a goal are done, I archive it. &lt;code&gt;/opgl:goal archive&lt;/code&gt; moves GOAL.md and DESIGN.md, and PLAN.md and CHECKPOINT.md if they exist, into docs/archive/goals/. If there is a roadmap, its milestone is marked done too.&lt;/p&gt;

&lt;p&gt;An archived goal is still used after that. When I later fix that part or work on the next goal, the AI finds the design, decisions, and checkpoints from that time in the archive and uses them as context. OpenGoal does not read the archive automatically. The archive stays in a fixed place, and CONTEXT.md or the next goal's documents often point to it, so the AI finds and reads it when needed.&lt;/p&gt;

&lt;p&gt;You can save the state of a goal in progress as a checkpoint. I mostly use checkpoints to prepare for compaction, or to hand work over to another session or another AI tool. A checkpoint often keeps details that the compaction summary misses. Compaction shrinks the whole conversation at once, but a checkpoint writes down what the AI has found so far and the next steps separately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: /opgl:goal checkpoint
AI:  ✓ docs/CHECKPOINT.md saved
     (If the session drops, the next one picks up where you left off)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checkpoints are especially useful when you use several AI tools together. I subscribe to Claude Code, Codex, and Antigravity, and I use them together. When I write a checkpoint in Claude Code, move to Codex and just run &lt;code&gt;/opgl:go&lt;/code&gt;, the work continues where it stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Natural language and other features
&lt;/h2&gt;

&lt;p&gt;You do not have to type the commands every time. When the context is clear, natural language works too. You can answer a recommended command with "ok" or "go", "proceed with task 6" runs &lt;code&gt;/opgl:go&lt;/code&gt;, and "save progress" runs &lt;code&gt;/opgl:goal checkpoint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Beyond that, through my long dogfooding, OpenGoal came to handle suspending and resuming goals, sub-goals, a backlog, and so on. I squeezed as much as I could out of markdown alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token use
&lt;/h2&gt;

&lt;p&gt;Overall, OpenGoal was also designed to reduce the use of our precious tokens. CONTEXT.md plays an important part in that. The AI knows in advance where things are, so there are fewer tool calls, and it needs to re-explore the codebase less at the start of each session. Also, there are no hooks or MCP servers, so unused tool definitions do not take up tokens. The subagent skill that installs with it serves the same purpose. The main agent decides, by the difficulty of the task, which model to hand a subagent, and it makes very small fixes itself. Subagents gather the material for analysis, but the main agent does the analysis itself. I had the problem in other harnesses where the main agent lost the fine details of the context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why markdown alone
&lt;/h2&gt;

&lt;p&gt;The biggest reason I built OpenGoal was this thought: all this machinery, built for a perfection we want to rely on, might become mostly unnecessary as AI improves quickly, and then markdown alone might be enough. And I think that has happened. I have finished various projects with only OpenGoal, from a small Obsidian plugin, to a native mobile editor with Notion-style block editing, to a state machine designer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Using subagents as parallel perspectives on design decision</title>
      <dc:creator>San</dc:creator>
      <pubDate>Mon, 13 Apr 2026 13:43:28 +0000</pubDate>
      <link>https://dev.to/san-tekart/using-subagents-as-parallel-perspectives-on-design-decision-50l2</link>
      <guid>https://dev.to/san-tekart/using-subagents-as-parallel-perspectives-on-design-decision-50l2</guid>
      <description>&lt;p&gt;Most of us use Claude Code subagents for parallel &lt;em&gt;work&lt;/em&gt; — search this,&lt;br&gt;
refactor that, concurrent execution. Lately I've been using them for&lt;br&gt;
something different: parallel &lt;em&gt;perspectives&lt;/em&gt; on the same problem.&lt;/p&gt;

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

&lt;p&gt;If you've discussed design decisions with the main agent long enough, you've hit this: the agent gets buried in the context, or the conversation grows long and it just starts agreeing with you. Either way, fresh angles stop coming.&lt;/p&gt;

&lt;p&gt;Subagents don't share that context. They come in cold — no history of the conversation, no accumulated framing — and that's exactly why they work here.&lt;/p&gt;
&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;The trigger for me was designing the commands for a lightweight AI harness called Scaff I've been building as a side project.&lt;/p&gt;

&lt;p&gt;I'd frequently hit walls while discussing the design with the main agent. When I asked for its take, it either couldn't break out of the framing it had built up so far, or it just drifted toward accommodating whatever I wanted.&lt;/p&gt;

&lt;p&gt;So I asked the main agent to do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"Log the current discussion to &lt;code&gt;docs/discussion/&amp;lt;topic&amp;gt;.md&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;"Spawn 3 subagents with different perspectives — LLM, software architect, end-user. Write each take to &lt;code&gt;...-round-1-&amp;lt;perspective&amp;gt;.md&lt;/code&gt;."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   docs/discussion/&amp;lt;topic&amp;gt;-round-1-llm.md
   docs/discussion/&amp;lt;topic&amp;gt;-round-1-architect.md
   docs/discussion/&amp;lt;topic&amp;gt;-round-1-enduser.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Resume the discussion with the main agent using the round-1 files as fresh inputs.&lt;/li&gt;
&lt;li&gt;If it converges, ask the main agent to update the original topic doc.&lt;/li&gt;
&lt;li&gt;If open issues remain, repeat the same cycle as &lt;code&gt;...-round-2.md&lt;/code&gt;, &lt;code&gt;...-round-3.md&lt;/code&gt; — until it actually closes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Three perspectives
&lt;/h2&gt;

&lt;p&gt;The key is the three parallel perspectives in step 2.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLM perspective: "Is this easy for the model to parse?"&lt;/li&gt;
&lt;li&gt;Architect perspective: "Will this design still hold up in a year?"&lt;/li&gt;
&lt;li&gt;End-user perspective: "Can I understand this in five minutes?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The three subagents come at the problem from genuinely different angles. More detail below.&lt;/p&gt;

&lt;p&gt;The side effect is great too.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;...-round-1.md&lt;/code&gt;, &lt;code&gt;...-round-2.md&lt;/code&gt; files persist as files, so the discussion doesn't evaporate from the conversation — it &lt;em&gt;accumulates as documentation&lt;/em&gt;. Three days later when you ask "wait, why did I settle on this?" — the answer is right there.&lt;/p&gt;

&lt;p&gt;Leaving a trace of decisions — that alone makes the pattern worth using.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it played out
&lt;/h2&gt;

&lt;p&gt;Here's how it played out on one design question. I can't include everything, but I've excerpted the moments that were most interesting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Topic:&lt;/strong&gt; Should we introduce a separate command for loading &lt;code&gt;OVERVIEW.md&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User:&lt;/strong&gt; What if the &lt;code&gt;/scaff:scout&lt;/code&gt; command auto-loaded &lt;code&gt;OVERVIEW.md&lt;/code&gt; — a project-level overview doc — on every session start? What do you think?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main agent:&lt;/strong&gt; Good direction. Having the overview available from the start would give better architectural grounding for every task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User:&lt;/strong&gt; But scout is often the starting point for a work session, and OVERVIEW.md can get long. End users might feel Scaff is heavier than its philosophy suggests. What about a dedicated &lt;code&gt;/scaff:overview load&lt;/code&gt; so it's only pulled in when needed?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main agent:&lt;/strong&gt; Good point. A &lt;code&gt;/scaff:overview load&lt;/code&gt; subcommand that loads on demand would help save tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User:&lt;/strong&gt; But for most other commands that work fine without it, that's extra token cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main agent:&lt;/strong&gt; If we just give &lt;code&gt;/scaff:scout&lt;/code&gt; an affordance — let it know &lt;code&gt;docs/OVERVIEW.md&lt;/code&gt; exists and let the agent decide whether to read it — that would solve the unnecessary token cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User:&lt;/strong&gt; But in my experience, the LLM will be biased toward reading &lt;code&gt;OVERVIEW.md&lt;/code&gt; anyway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User:&lt;/strong&gt; Let's hear from the subagents. Log the current discussion to &lt;code&gt;docs/discussion/&amp;lt;topic&amp;gt;.md&lt;/code&gt;. Then spawn LLM / software architect / end-user subagents and write each take to &lt;code&gt;...-round-1-&amp;lt;perspective&amp;gt;.md&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  LLM perspective
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The bias is real, and the user has correctly diagnosed it.&lt;/em&gt; LLMs trained on helpfulness are systematically biased toward "more context = better answer." The model's internal cost function treats a false negative (missed read) as far more painful than a false positive (unnecessary read). Skipping feels like negligence, reading feels like diligence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(...it walked through concrete examples and estimated the soft rule would fire on 6–8 out of 10 typical invocations.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gating must key off &lt;em&gt;literal tokens in the user's input&lt;/em&gt;, not the model's interpretation of task semantics. Interpretation always drifts toward "read more."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Architect perspective
&lt;/h3&gt;

&lt;p&gt;The architect didn't touch execution at all. It went after the document structure: CONTEXT.md is "what's being worked on right now," OVERVIEW.md is "big picture that rarely changes" — but read literally, that's exactly what you'd want at session start.&lt;/p&gt;

&lt;p&gt;(...it re-diagnosed this as a definitional collision, not a loading problem.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If OVERVIEW.md cannot answer "who reads me, when, and why," the fix is to delete the role, not to invent a loader for it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same topic, completely different layers. The LLM pointed at execution bias. The architect pointed at "should this document even exist?"&lt;/p&gt;

&lt;h3&gt;
  
  
  End-user perspective
&lt;/h3&gt;

&lt;p&gt;The end-user perspective came in from yet another angle — user behavior:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A soft rule like "read OVERVIEW.md when the task touches architecture" sounds disciplined on paper, but in practice the LLM's threshold for "touches architecture" is fuzzy and self-serving — when in doubt, it reads.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(...after honestly checking how often users actually ask "what's the big picture?" mid-workflow — rarely — it concluded that frequency doesn't justify automation.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reject the soft rule entirely. That phrasing is exactly the fuzzy trigger the user is warning about, and it will collapse into always-load within a week of real use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What converged
&lt;/h2&gt;

&lt;p&gt;After the three perspectives came back, I resumed with the main agent. The architect's "role collision" diagnosis turned out to be a naming collision — CONTEXT.md's first heading was &lt;code&gt;# Project Overview&lt;/code&gt;, which made the two docs look like they overlapped. Renaming it to &lt;code&gt;# Working Context&lt;/code&gt; fixed it. Subagents can misdiagnose too, but even that surfaced the real issue.&lt;/p&gt;

&lt;p&gt;The soft rule was scrapped. All three perspectives rejected it, and I agreed. OVERVIEW.md loading switched to reactive triggers — it only suggests loading when specific events fire, not on every session. The user decides.&lt;/p&gt;

&lt;p&gt;Then a new problem: where does this reactive-trigger principle live? The main agent suggested scaff-subagent, but that skill is specifically for subagent delegation. Deciding when to read OVERVIEW.md is main-agent workflow, not subagent work. So we created the scaff-flow skill.&lt;/p&gt;

&lt;p&gt;Once scaff-flow existed, we noticed document sync guidelines scattered across individual command files — when to suggest /scaff:design sync, when to suggest /scaff:context sync — had the same character. Those went into scaff-flow too. In the end, scaff-flow became a collection of principles for the main agent to autonomously drive a scaff project — a skill that should work better as AI improves.&lt;/p&gt;

&lt;p&gt;The original discussion was "when should OVERVIEW.md be read?" It ended with a new skill that collects main-agent workflow principles — and should age well as models get better at autonomous decisions.&lt;/p&gt;

&lt;p&gt;Scaff is &lt;a href="https://github.com/opellen/scaff" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt; if you want to look around.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building Real-time Web Applications with PynneX and FastAPI</title>
      <dc:creator>San</dc:creator>
      <pubDate>Sun, 02 Feb 2025 07:35:23 +0000</pubDate>
      <link>https://dev.to/san-tekart/fastapisocketio-stock-monitor-with-python-workers-3pfc</link>
      <guid>https://dev.to/san-tekart/fastapisocketio-stock-monitor-with-python-workers-3pfc</guid>
      <description>&lt;h1&gt;
  
  
  Building Real-time Web Applications with PynneX and FastAPI
&lt;/h1&gt;

&lt;p&gt;I've created three examples demonstrating how to build real-time web applications using Python worker threads and event-driven patterns. Rather than fully replacing established solutions like Celery or Redis, this approach aims to offer a lighter alternative for scenarios where distributed task queues may be overkill. No locks, no manual concurrency headaches — just emitters in the worker and listeners on the main thread or other workers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why PynneX?
&lt;/h2&gt;

&lt;p&gt;While there are several solutions for handling concurrent tasks in Python, each comes with its own trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Celery&lt;/strong&gt;: Powerful for distributed tasks but might be overkill for simpler scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt;: Great as an in-memory data store, though it adds external dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RxPY&lt;/strong&gt;: Comprehensive reactive programming but has a steeper learning curve&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;asyncio.Queue&lt;/strong&gt;: Basic but needs manual implementation of high-level patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qt's Signals &amp;amp; Slots&lt;/strong&gt;: xcellent pattern but tied to GUI frameworks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PynneX takes the proven emitter-listener pattern and makes it seamlessly work with asyncio for general Python applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt;: No external dependencies beyond Python stdlib&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focused&lt;/strong&gt;: Designed specifically for thread-safe communication between threads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple&lt;/strong&gt;: Clean and intuitive through declarative event handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible&lt;/strong&gt;: Not tied to any UI framework or architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For simpler scenarios where you just need clean thread communication without distributed task queues, PynneX provides a lightweight alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  🍓 1. Berry Checker (Basic)
&lt;/h2&gt;

&lt;p&gt;A minimal example showing the core concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker thread for background processing&lt;/li&gt;
&lt;li&gt;WebSocket real-time updates&lt;/li&gt;
&lt;li&gt;Event-driven task handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/dOYNtHUgAvA" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/dOYNtHUgAvA" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9jfk5dr7brm51z7k77aq.jpg" alt="Demo" width="320" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nexconnectio/pynnex/blob/main/examples/fastapi_socketio_simple.py" rel="noopener noreferrer"&gt;View Code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📱 2. QR Code Generator (Intermediate)
&lt;/h2&gt;

&lt;p&gt;Building on the basic concepts and adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time image generation&lt;/li&gt;
&lt;li&gt;Base64 image encoding/decoding&lt;/li&gt;
&lt;li&gt;Clean Controller-Worker pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thread safety comes for free: the worker generates QR codes and emits them, the main thread listens and updates the UI. No manual synchronization needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/Yr5t6143sbQ" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/Yr5t6143sbQ" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8z3pynvjwc5un8jb4rg.jpg" alt="Demo" width="320" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nexconnectio/pynnex/blob/main/examples/fastapi_socketio_stock_monitor.py" rel="noopener noreferrer"&gt;View Code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📈 3. Stock Monitor (Advanced)
&lt;/h2&gt;

&lt;p&gt;A full-featured example showcasing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple worker threads&lt;/li&gt;
&lt;li&gt;Interactive data grid (ag-Grid)&lt;/li&gt;
&lt;li&gt;Real-time charts (eCharts)&lt;/li&gt;
&lt;li&gt;Price alert system&lt;/li&gt;
&lt;li&gt;Clean architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/SHthW2KCtmM" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtube.com/shorts/SHthW2KCtmM" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnmq9baz3lhqntsnce4lc.jpg" alt="Demo" width="320" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nexconnectio/pynnex/blob/main/examples/fastapi_socketio_stock_monitor.py" rel="noopener noreferrer"&gt;View Code&lt;/a&gt;&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Clone repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/nexconnectio/pynnex.git
&lt;span class="nb"&gt;cd &lt;/span&gt;pynnex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install dependencies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;fastapi python-socketio uvicorn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run any example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python examples/fastapi_socketio_simple.py
python examples/fastapi_socketio_qr.py
python examples/fastapi_socketio_stock_monitor.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt; in your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python worker threads for background processing&lt;/li&gt;
&lt;li&gt;WebSocket for real-time updates&lt;/li&gt;
&lt;li&gt;Event-driven architecture with emitter-listener pattern&lt;/li&gt;
&lt;li&gt;Clean separation of concerns&lt;/li&gt;
&lt;li&gt;No complex dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technical Details
&lt;/h2&gt;

&lt;p&gt;PynneX provides a lightweight layer for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;emitter-listener pattern for event handling&lt;/li&gt;
&lt;li&gt;Worker thread management&lt;/li&gt;
&lt;li&gt;Thread-safe task queuing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FastAPI for the web framework&lt;/li&gt;
&lt;li&gt;SocketIO for WebSocket communication&lt;/li&gt;
&lt;li&gt;Python's built-in threading and asyncio&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nexconnectio/pynnex/tree/main/docs" rel="noopener noreferrer"&gt;PynneX Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nexconnectio/pynnex" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/pynnex" rel="noopener noreferrer"&gt;PyPI Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The examples above demonstrate how to build real-time web applications with clean thread communication patterns, without the complexity of traditional task queue systems.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>asyncio</category>
      <category>fastapi</category>
      <category>socketio</category>
    </item>
  </channel>
</rss>
