<?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.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>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>
