<?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: Sam Novak</title>
    <description>The latest articles on DEV Community by Sam Novak (@sam_novak_574b07811e18495).</description>
    <link>https://dev.to/sam_novak_574b07811e18495</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%2F3844731%2F3626a15e-2f42-4123-8176-443a2c43928c.png</url>
      <title>DEV Community: Sam Novak</title>
      <link>https://dev.to/sam_novak_574b07811e18495</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sam_novak_574b07811e18495"/>
    <language>en</language>
    <item>
      <title>Call whoami first: your agent should not trust a cached tool catalog</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Thu, 27 Aug 2026 08:13:54 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/call-whoami-first-your-agent-should-not-trust-a-cached-tool-catalog-1gkp</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/call-whoami-first-your-agent-should-not-trust-a-cached-tool-catalog-1gkp</guid>
      <description>&lt;p&gt;There is a failure I have now watched three separate teams hit, and it always looks like a bug in the agent.&lt;/p&gt;

&lt;p&gt;An agent connects to a tool server, does useful work for a week, and then one day starts producing confidently wrong actions. It calls a tool that no longer exists. It tries to transition something it is not allowed to transition. It reports success on a mutation that silently did nothing. Everyone goes looking for the regression in the model, or in the prompt, and the actual cause is that the agent was working from a catalog of tools and permissions it had cached at some point in the past.&lt;/p&gt;

&lt;p&gt;The fix is one boring call at the start of every session, and it is worth understanding why it is not optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  A token is a connection, not a role
&lt;/h2&gt;

&lt;p&gt;Here is the thing people get wrong about tokens on a permissioned tool server. The token identifies &lt;em&gt;which named connection&lt;/em&gt; is talking. It does not carry the caller's authority.&lt;/p&gt;

&lt;p&gt;That sounds like a distinction without a difference until something changes on the other side. The person the connection belongs to gets moved to a different team. Their role changes. An admin turns off tool access at the workspace level. A permission that used to be granted is revoked.&lt;/p&gt;

&lt;p&gt;In a system built correctly, the token keeps working as an identifier and the &lt;em&gt;authority&lt;/em&gt; is resolved fresh on every call from the person's current membership, roles, and permissions. Which means the answer to "can I do this?" can be yes on Monday and no on Tuesday with nothing about the token having changed. Wagglet's MCP server is explicit about this: a token identifies a named connection, and it never overrides the same person's current membership, roles, or permissions. Team-level access is off by default, and disabling it later rejects every connection request immediately without deleting the connections.&lt;/p&gt;

&lt;p&gt;If your agent cached a permission list at connection time, it is now confidently wrong about what it can do, and the only symptom you will see is a failed action it did not expect to fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool schemas move under you
&lt;/h2&gt;

&lt;p&gt;The second half is the tool catalog. Any actively developed tool server adds tools, renames arguments, tightens validation, and deprecates things. That is normal and healthy.&lt;/p&gt;

&lt;p&gt;What is not healthy is an agent whose idea of the available tools came from documentation, a blog post, or a previous session's transcript. A copied old catalog is not a source of truth. The live &lt;code&gt;tools/list&lt;/code&gt; response is. This is the same discipline as not hardcoding an API response shape you saw once in a tutorial, except the failure is quieter, because a model will happily improvise a plausible call for a tool that no longer accepts that argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  So: whoami first, then read, then act
&lt;/h2&gt;

&lt;p&gt;The sequence that holds up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Establish identity and authority.&lt;/strong&gt; Call the server's &lt;code&gt;whoami&lt;/code&gt; (or equivalent) and treat its team, identity, roles, and permissions as the authority for this session. Not your prompt's belief about who you are. Not last week's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Fetch the live tool schemas.&lt;/strong&gt; Use them, not a remembered list. If a tool you were planning to call is gone, that is information, not an error to work around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Discover ids with bounded search, and page properly.&lt;/strong&gt; Cursors exist because the population can be bigger than one response. An agent that reads the first page and reports a total is producing a confident number that is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Read the complete authorized record before you change it.&lt;/strong&gt; Fetch the ticket or document, keep the returned record and its item revisions, and use those in the mutation. This is what makes concurrent edits safe instead of last-write-wins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Use the matching lifecycle command, and give each intended mutation a fresh operation id.&lt;/strong&gt; Reuse an operation id only when you are deliberately retrying &lt;em&gt;the same&lt;/em&gt; intended change. This is the difference between a retry and a duplicate.&lt;/p&gt;

&lt;p&gt;The full sequence with the actual tool names is in the &lt;a href="https://wagglet.com/docs/mcp" rel="noopener noreferrer"&gt;Wagglet MCP workspace guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is more than hygiene
&lt;/h2&gt;

&lt;p&gt;There is a design point buried in this that is worth pulling out.&lt;/p&gt;

&lt;p&gt;A permissioned tool interface is not a database with a chat wrapper. It keeps the product's own language and its own lifecycle rules, and it refuses actions that do not fit them. That refusal is the feature. An agent that could freely write any field would produce records that are structurally valid and semantically nonsense - a task marked delivered with no evidence, a review verdict with no reviewer.&lt;/p&gt;

&lt;p&gt;Which is why "read current state, then use the explicit action for what you actually mean" is not ceremony. It is the thing that keeps an agent's output reviewable by a human afterwards. If you want the reasoning behind treating delivery, acceptance, merge, and deploy as four separate facts rather than one status field, that is the argument in &lt;a href="https://wagglet.com/blog/wagglet-workflow-request-draft-ticket-delivery" rel="noopener noreferrer"&gt;the Wagglet workflow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line version
&lt;/h2&gt;

&lt;p&gt;Do not let an agent start from what it remembers. Make it ask who it is, what it may do, and what tools exist - every session, out loud, before the first action. It costs one call. It saves the class of bug that looks like the model got worse and is actually your permissions changing underneath a cached answer.&lt;/p&gt;

&lt;p&gt;Background on the surrounding handoff model: &lt;a href="https://wagglet.com/how-it-works" rel="noopener noreferrer"&gt;how Wagglet works&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your coding agent needs a read-only, task-scoped, expiring credential</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Thu, 27 Aug 2026 08:07:58 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/your-coding-agent-needs-a-read-only-task-scoped-expiring-credential-2o2l</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/your-coding-agent-needs-a-read-only-task-scoped-expiring-credential-2o2l</guid>
      <description>&lt;p&gt;Every team that starts handing work to coding agents hits the same wall in about week three. Someone prepares a task, someone else is going to run it, and the agent needs context. Not vague context - the ticket, the acceptance criteria, the linked spec, the three comments that changed the scope. So one of two things happens.&lt;/p&gt;

&lt;p&gt;Either the person who prepared the task pastes a wall of text into a chat window and hopes it is still accurate by the time the agent reads it, or somebody shares a credential. A personal access token. An API key with read/write on the whole workspace. In the worst version I have seen, a shared login to the tracker itself.&lt;/p&gt;

&lt;p&gt;Both are bad, and they are bad in ways that do not look like security problems at first. They look like reliability problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The snapshot goes stale, quietly
&lt;/h2&gt;

&lt;p&gt;A pasted brief is a snapshot. It was true when it was copied. Ten minutes later somebody narrows the scope in a comment, or attaches the design that answers the open question, and the agent has no way to know. It will complete the task it was given, confidently, and you will find out at review that it was the previous version of the task.&lt;/p&gt;

&lt;p&gt;The failure mode is not that the agent is wrong. It is that nothing in the system can tell you the agent was reading a stale copy. There is no version, no timestamp, no way to ask "is this still current?".&lt;/p&gt;

&lt;h2&gt;
  
  
  The credential is too big, always
&lt;/h2&gt;

&lt;p&gt;So the obvious fix is to give the agent live access. And the moment you do that with a normal token, you have handed a process that generates text the ability to change your workspace.&lt;/p&gt;

&lt;p&gt;Think about what a standard workspace token can usually do: read any ticket, comment anywhere, transition anything, sometimes delete. You needed exactly one of those powers - read this one task and the things it points at. You granted all of them, for as long as the token lives, which is usually forever.&lt;/p&gt;

&lt;p&gt;And you granted them to the wrong identity. A shared token is not "the agent" or "the runner", it is whoever created the token. Every action lands in the audit log under their name. Six weeks later, when you are trying to work out who moved the ticket, the log says a person who was on holiday.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a bounded credential looks like
&lt;/h2&gt;

&lt;p&gt;The version that actually works is narrower on four separate axes, and it is worth being explicit about all four, because most homegrown solutions get two right and forget the others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read-only.&lt;/strong&gt; Not "read-mostly". The capability the agent starts with should have no ability to deliver, transition, comment, or edit anything. If the agent's report is going to move the task forward, that should be a separate, later, deliberately authorized action - not something the same credential could do at any moment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rooted at one resource.&lt;/strong&gt; Scoped to this claim, on this task. Not to the project, not to the team. If the agent follows a link out of the task to something it was not granted, the answer should be a refusal, not a helpful response. This is the axis people skip, because project-wide scoping is so much easier to build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expiring.&lt;/strong&gt; Days, not months. The task will be done or abandoned long before then. A credential that outlives the work it was minted for is just a key under the doormat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revocable independently.&lt;/strong&gt; Turning off agent access for the team should reject connections immediately, without deleting them and without anybody having to hunt down individual tokens.&lt;/p&gt;

&lt;p&gt;Wagglet's handoff is built on exactly this shape: when a teammate claims a task, the copied prompt carries a read-only, claim-rooted context capability plus a fallback snapshot. The agent can refresh the permitted current context - so the staleness problem goes away - but that starting capability cannot deliver or change anything. The details are in the &lt;a href="https://wagglet.com/docs/mcp" rel="noopener noreferrer"&gt;MCP workspace guide&lt;/a&gt;, and the surrounding flow is on &lt;a href="https://wagglet.com/how-it-works" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity stays with the person
&lt;/h2&gt;

&lt;p&gt;The other half of this, which is easy to miss: the runner uses their own agent subscription. Their own Claude Code or Codex account, their own token balance. Nobody shares an agent login to move a task, which is the practice this whole pattern exists to kill.&lt;/p&gt;

&lt;p&gt;That matters for a boring reason and an interesting one. The boring reason is terms of service. The interesting one is that "who ran this" and "who prepared this" are genuinely different facts, and a shared login collapses them into one name. Once collapsed, you cannot answer basic questions about your own process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheap version, if you are not adopting anything
&lt;/h2&gt;

&lt;p&gt;You do not need a product to get most of this. If you are rolling your own handoff today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mint a token per task, not per person, and give it read scope only.&lt;/li&gt;
&lt;li&gt;Set an expiry you would be comfortable defending, then halve it.&lt;/li&gt;
&lt;li&gt;Put the resource id in the token's scope and enforce it server-side, not in the prompt. A prompt instruction to "only read ticket 412" is a suggestion.&lt;/li&gt;
&lt;li&gt;Log the human runner as the actor for anything the agent causes, and keep the agent's own report as evidence rather than as a state change.&lt;/li&gt;
&lt;li&gt;Make the delivery step a separate call with a separate authorization, so "the agent said it is done" and "the task is done" stay different facts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is not complicated. It is just narrower than the credential you already have lying around, which is why almost everybody reaches for the wrong one first.&lt;/p&gt;

&lt;p&gt;More on the task-design side of this: &lt;a href="https://wagglet.com/blog/dual-prompt-human-agent-task-design" rel="noopener noreferrer"&gt;the dual prompt&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>Delivered, accepted, merged, deployed: four facts your tracker calls Done</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Wed, 26 Aug 2026 06:40:23 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/delivered-accepted-merged-deployed-four-facts-your-tracker-calls-done-3nfh</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/delivered-accepted-merged-deployed-four-facts-your-tracker-calls-done-3nfh</guid>
      <description>&lt;p&gt;Most issue trackers have a column called "Done." It is doing far too much work.&lt;/p&gt;

&lt;p&gt;"Done" is quietly standing in for at least four separate facts, each with a different owner, a different moment, and a different failure mode:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The person who did the work &lt;strong&gt;says&lt;/strong&gt; it is finished.&lt;/li&gt;
&lt;li&gt;Someone responsible for the outcome &lt;strong&gt;accepts&lt;/strong&gt; that claim.&lt;/li&gt;
&lt;li&gt;The change is &lt;strong&gt;merged&lt;/strong&gt; into the branch that matters.&lt;/li&gt;
&lt;li&gt;The change is &lt;strong&gt;deployed&lt;/strong&gt; where users can reach it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When a workflow collapses these into one status, it doesn't make the process simpler. It makes the process &lt;em&gt;unable to describe what actually happened&lt;/em&gt;. And you notice the missing vocabulary at the worst possible moment, usually in an incident channel at 6pm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the collapse hurts
&lt;/h2&gt;

&lt;p&gt;Each of the four facts can be true while the others are false. All of these are real states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reported but not accepted.&lt;/strong&gt; The runner believes the task is complete. The reviewer hasn't looked yet, or looked and disagreed. The work may be genuinely wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accepted but not merged.&lt;/strong&gt; A reviewer signed off on a branch that then sat for nine days behind a conflict nobody owned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merged but not deployed.&lt;/strong&gt; The code is in &lt;code&gt;main&lt;/code&gt;. &lt;code&gt;main&lt;/code&gt; last shipped on Thursday. It is Tuesday.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployed but not accepted.&lt;/strong&gt; The riskiest one. Something reached production without anyone qualified confirming it does the right thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single "Done" column cannot express any of these. So teams invent shadow vocabulary to compensate - "done done", "really done", "done but not shipped", a comment thread, a Slack message that scrolls away. That shadow vocabulary is a symptom. The status model is missing states the work actually has.&lt;/p&gt;

&lt;h2&gt;
  
  
  This gets worse when an agent did the work
&lt;/h2&gt;

&lt;p&gt;Everything above predates AI tooling. But handing work to a coding agent sharpens it considerably, for one specific reason: &lt;strong&gt;an agent will always produce a confident report.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A human who half-finished a task tends to leak signal. They hedge. They say "I think it works but I couldn't test the mobile case." An agent, asked to summarize what it did, generally returns a clean, plausible, well-structured account of having succeeded. That report is a genuinely useful artifact - but it is &lt;em&gt;evidence&lt;/em&gt;, not a verdict.&lt;/p&gt;

&lt;p&gt;If your workflow treats "the agent reported completion" as equivalent to "the task is complete," you have wired a system where the party doing the work also certifies the work, and the certifying party is the one component in the loop that is structurally incapable of expressing doubt about its own output.&lt;/p&gt;

&lt;p&gt;That is not an argument against agent-run tasks. It is an argument for keeping delivery and acceptance as two separate events with two different owners.&lt;/p&gt;

&lt;h2&gt;
  
  
  What separating them looks like
&lt;/h2&gt;

&lt;p&gt;The practical version is not heavy. It's mostly about naming the transitions honestly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delivery is a report, not a verdict.&lt;/strong&gt; Whoever performed the work - human, agent, or the pair - reports what happened, what evidence exists, and what they could not verify. Ideally the "what I could not verify" section is mandatory, because it is the section that actually carries information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acceptance is a decision by someone accountable for the outcome.&lt;/strong&gt; They can accept, send it back to the runner, or reopen it. Crucially they must be able to &lt;em&gt;judge&lt;/em&gt; the work. If nobody in the loop can distinguish correct output from confidently wrong output, acceptance is theater, and you've distributed risk rather than work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge and deploy stay their own facts.&lt;/strong&gt; They're usually visible in tooling already; the mistake is letting either one imply acceptance. A revert is a normal event, and a status model where "deployed" is terminal makes reverting feel like failure rather than routine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop conditions are defined up front.&lt;/strong&gt; The task should say what to do when authority or evidence is missing. Both a human and an agent should be able to stop and escalate rather than guess. An agent that hits an ambiguous requirement and picks an interpretation has made a product decision nobody delegated to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheap version
&lt;/h2&gt;

&lt;p&gt;You don't need new tooling to get most of the value. Try this on your next handed-off task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write down what "accepted" means &lt;em&gt;before&lt;/em&gt; the work starts. If you can't, the task isn't ready to hand to anyone, agent or human.&lt;/li&gt;
&lt;li&gt;Name the reviewer by name, not by role.&lt;/li&gt;
&lt;li&gt;Require the delivery report to include an explicit "not verified" list.&lt;/li&gt;
&lt;li&gt;Never let the person or process that performed the work also mark it accepted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the whole thing, really. Everything else is elaboration.&lt;/p&gt;




&lt;p&gt;I work on &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt;, where this separation is built into the task lifecycle - the participant who ran the task reports it, which moves the task to Delivered, and an authorized reviewer separately accepts it, sends it back, or reopens it. The full five-stage model is written up at &lt;a href="https://wagglet.com/how-it-works" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;, and the handoff mechanics are in the &lt;a href="https://wagglet.com/docs/task-handoff" rel="noopener noreferrer"&gt;task handoff docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Curious how others handle this: does your tracker distinguish delivered from accepted, or did your team also end up inventing "done done"?&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>The skill gap blocking a task is usually about twenty minutes wide</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Wed, 26 Aug 2026 06:37:32 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/the-skill-gap-blocking-a-task-is-usually-about-twenty-minutes-wide-2i73</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/the-skill-gap-blocking-a-task-is-usually-about-twenty-minutes-wide-2i73</guid>
      <description>&lt;p&gt;The most expensive sentence in a small company is some version of this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"She would be perfect for this, if only she knew how to set up the project."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I have watched this block real work more times than I can count. An office manager who had the context and the patience to reproduce a nasty intermittent bug - but not the local toolchain to reach it. An artist whose eye was exactly what a feature needed - who could not run &lt;code&gt;npm install&lt;/code&gt; against a private registry. A teammate who could tell you in ten seconds whether a workflow made sense, but who had never written a prompt that a coding agent could act on.&lt;/p&gt;

&lt;p&gt;None of that is a story about talent. It is a story about &lt;strong&gt;where&lt;/strong&gt; the gap sits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap is at the entrance, so the whole task waits
&lt;/h2&gt;

&lt;p&gt;Here is the structural problem. The missing knowledge is usually small - twenty minutes of tooling trivia. Which command starts the app. Where the staging test account lives. What a good delivery report looks like.&lt;/p&gt;

&lt;p&gt;But that small gap sits at the &lt;em&gt;entrance&lt;/em&gt; to the task. And a gap at the entrance doesn't cost you twenty minutes; it costs you the entire task, because the task now has to wait for one of the three people who can cross the threshold unaided. Those three people are, of course, the people who are already the bottleneck for everything else.&lt;/p&gt;

&lt;p&gt;So the work queues behind them. Not because it is hard. Because it is &lt;em&gt;unenterable&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Can this person do engineering?" is the wrong question
&lt;/h2&gt;

&lt;p&gt;We ask it constantly, and it's malformed. "Engineering" isn't one act. Shipping a change involves at least: understanding a requirement, locating the relevant code, changing it, running it, noticing a visual defect, checking permissions, testing an edge case, and deciding whether the result is acceptable.&lt;/p&gt;

&lt;p&gt;Those are genuinely different skills, and almost nobody is uniformly strong across all of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A teammate unqualified to design an authorization system may still be the best person on the team at noticing that a customer flow &lt;em&gt;feels&lt;/em&gt; broken.&lt;/li&gt;
&lt;li&gt;An artist who doesn't know your build tool is very likely your strongest judge of whether a transition regressed.&lt;/li&gt;
&lt;li&gt;An ops teammate who will never write the fix often knows the only reliable sequence that reproduces the bug.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you stop treating "engineering" as a single competency, a task stops being all-or-nothing and starts being decomposable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decompose the task into four parts, not two
&lt;/h2&gt;

&lt;p&gt;The instinct is to split work into "the technical part" and "the rest." That's too coarse. In practice a bounded task becomes transferable when you separate four distinct things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The domain brief.&lt;/strong&gt; Somebody who actually understands the problem writes down the outcome, the constraints, and the boundaries. What "done" means. What is explicitly out of scope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The agent guidance.&lt;/strong&gt; The coding agent gets technical context, setup instructions, the work request, and - critically - stop conditions. When should it refuse to continue and ask?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The human runbook.&lt;/strong&gt; The person running the task gets a short, concrete list: which access to use, what to observe, what judgment only they can supply, what to QA, and when to escalate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The acceptance step.&lt;/strong&gt; Evidence goes back to a qualified reviewer who decides whether the thing is actually complete. This is a &lt;em&gt;separate decision&lt;/em&gt; from "the work was performed."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;domain expert's brief + technical context
                |
                v
   human runner  &amp;lt;--&amp;gt;  coding agent
   access, eyes         setup, implementation,
   judgment             explanation
                |
                v
     evidence + recorded delivery
                |
                v
     qualified review and acceptance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what this is &lt;em&gt;not&lt;/em&gt;. It is not "give the task to somebody cheaper and hope the model covers the difference." The expert's knowledge stays in the system in two places: once in the brief, and again at review. The runner's role is real rather than ceremonial, because the task explicitly names a check they are equipped to perform.&lt;/p&gt;

&lt;h2&gt;
  
  
  The research warning is also the argument
&lt;/h2&gt;

&lt;p&gt;The strongest caution about this idea is also the clearest case for it.&lt;/p&gt;

&lt;p&gt;In a preregistered experiment, BCG gave consultants access to and training on GPT-4 for tasks outside their existing skill sets. On tasks that sat inside the model's competence, assisted consultants performed substantially better than unassisted ones. On tasks positioned just outside it - where the model was confidently wrong - assisted consultants did &lt;em&gt;worse&lt;/em&gt;, because they accepted plausible output they lacked the expertise to challenge.&lt;/p&gt;

&lt;p&gt;Read that carefully, because both halves matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Capability genuinely transfers. A non-specialist with a good agent and a good brief can complete real work.&lt;/li&gt;
&lt;li&gt;Capability transfers &lt;strong&gt;only up to the boundary of verification.&lt;/strong&gt; The moment nobody in the loop can tell good output from confidently wrong output, you have not distributed the work. You have distributed the risk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly why the fourth part - acceptance by a qualified reviewer - isn't bureaucratic overhead. It's the thing that makes the other three safe. Assisted performance is not the same as expertise, and a handoff design that forgets this converts a skill gap into a silent defect.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means practically
&lt;/h2&gt;

&lt;p&gt;If you want a task to be transferable, the work is mostly front-loaded and mostly writing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write the brief before you offer the task, not after someone claims it.&lt;/li&gt;
&lt;li&gt;Give the agent and the human &lt;em&gt;different&lt;/em&gt; instructions. One generic paragraph asks both of them to improvise.&lt;/li&gt;
&lt;li&gt;Name the specific human check the runner is qualified to perform. If you can't name one, the task probably shouldn't be handed off.&lt;/li&gt;
&lt;li&gt;Keep delivery and acceptance as two separate events. "The runner reported it" and "the reviewer accepted it" are different facts.&lt;/li&gt;
&lt;li&gt;Be honest about the verification boundary. Some work should not be handed off at all, and a better prompt cannot repair a missing qualified reviewer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The payoff isn't that job titles stop mattering. It's that a narrow twenty-minute gap stops being able to hold an entire task hostage.&lt;/p&gt;




&lt;p&gt;I work on &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt;, which is built around this pattern - an author prepares a task for two audiences, a teammate claims and runs it on their own agent subscription, and acceptance stays a separate decision. If you want the longer version of the argument above, it's written up as a field note: &lt;a href="https://wagglet.com/blog/close-team-skill-gaps-with-ai-task-handoffs" rel="noopener noreferrer"&gt;How AI task handoffs help more teammates complete technical work&lt;/a&gt;. The five-stage lifecycle is documented at &lt;a href="https://wagglet.com/how-it-works" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How does your team handle this? I'm curious whether anyone has found a good way to decide &lt;em&gt;in advance&lt;/em&gt; which tasks are safe to hand off.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Move the task, not the AI account</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Tue, 25 Aug 2026 09:19:04 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/move-the-task-not-the-ai-account-3pbj</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/move-the-task-not-the-ai-account-3pbj</guid>
      <description>&lt;p&gt;Sharing AI work across a team gets messy fast.&lt;/p&gt;

&lt;p&gt;One person writes a good prompt, gathers the context, gets partway through. Then someone else needs to continue. So fragments get pasted into Slack, screenshots get sent, a conversation gets exported, or an account is left signed in for the next person.&lt;/p&gt;

&lt;p&gt;The collaboration isn't the problem. The handoff is. Most AI tools assume one person, one account, one session. The moment work crosses that boundary, you have to decide what moves and what stays.&lt;/p&gt;

&lt;p&gt;Wagglet's rule is simple: &lt;strong&gt;move the prepared task, not the AI account.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That doesn't mean nothing is shared. An agent can't work on context it never receives. It means the disclosure can be about the job rather than somebody's identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What moves
&lt;/h2&gt;

&lt;p&gt;A real AI task is more than a one-line prompt. It usually includes the intended result, background and prior decisions, constraints and acceptance criteria, project or repository context, chosen attachments, discussion and rework feedback, and the evidence expected at delivery.&lt;/p&gt;

&lt;p&gt;That's the material that moves. One person prepares the task; another claims it and runs it with their own authenticated coding agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What stays
&lt;/h2&gt;

&lt;p&gt;Keeping each AI account with its owner preserves three boundaries worth having:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity.&lt;/strong&gt; Provider activity stays attached to the person who owns the account. A shared login can show that &lt;em&gt;an account&lt;/em&gt; acted, but not which teammate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions.&lt;/strong&gt; The runner works with the repository, files, and provider access already in their own environment. Claiming a task doesn't silently grant them the author's local permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage and billing.&lt;/strong&gt; The subscription, quota, and billing relationship stay with the person running the work.&lt;/p&gt;

&lt;p&gt;It's a policy boundary too. A handoff doesn't make provider-prohibited account sharing acceptable, and the runner still needs legitimate access to every system the task touches.&lt;/p&gt;

&lt;h2&gt;
  
  
  A handoff is still a disclosure
&lt;/h2&gt;

&lt;p&gt;This is the part worth stating plainly, because "move the task, not the account" is easy to over-read.&lt;/p&gt;

&lt;p&gt;If someone copies a task into Claude Code, Codex, or another agent, that task context is disclosed to that agent and processed under the provider's terms. Attachments and instructions deserve the same review as anything else you send to an AI provider.&lt;/p&gt;

&lt;p&gt;So before a handoff: strip unnecessary secrets and personal data, check every attachment belongs, and confirm the chosen agent may receive the material. When the answer is no, keep the task human-only or narrow it first.&lt;/p&gt;

&lt;p&gt;Wagglet reduces the need to share identity. It doesn't turn sensitive content into non-sensitive content.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a handoff does not transfer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;the sender's Google, GitHub, Claude, or OpenAI login&lt;/li&gt;
&lt;li&gt;their AI subscription or token balance&lt;/li&gt;
&lt;li&gt;their browser session&lt;/li&gt;
&lt;li&gt;a team model key or integration credential&lt;/li&gt;
&lt;li&gt;access to the sender's computer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A team can separately configure server-side integrations, and an MCP connection can grant an agent the product actions its owner is already authorized to perform. Those are explicit features with their own controls, not hidden extras attached to a single handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions worth settling before adopting anything like this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What may people put in it?&lt;/strong&gt; Decide whether customer data, production logs, or regulated information belong in tasks and attachments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who belongs in each workspace?&lt;/strong&gt; Membership, roles, admins, offboarding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which integrations get connected?&lt;/strong&gt; Treat GitHub, Slack, model keys and MCP connections as separate credentials with different scopes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What assurance does security require?&lt;/strong&gt; Wagglet does not currently claim SOC 2 or ISO 27001 certification, and a hosting provider's certifications don't transfer to the product running on it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Less exciting than asking whether a product "uses encryption", but the answers are more useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The point
&lt;/h2&gt;

&lt;p&gt;AI collaboration involves two assets: the work, and the identity used to execute it.&lt;/p&gt;

&lt;p&gt;The work often needs to move. The identity usually shouldn't.&lt;/p&gt;

&lt;p&gt;Details are on the &lt;a href="https://wagglet.com/security" rel="noopener noreferrer"&gt;Wagglet security page&lt;/a&gt; and the &lt;a href="https://wagglet.com/privacy" rel="noopener noreferrer"&gt;privacy page&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on Wagglet, which is operated by Rockbite Games. This describes the product's current documented behaviour and is not a claim of any security certification.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>One task, two readers:the agent, the human</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:54:01 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/one-task-two-readersthe-agent-the-human-2o00</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/one-task-two-readersthe-agent-the-human-2o00</guid>
      <description>&lt;p&gt;I spent a long time writing deliberately vague task descriptions, and I could defend it. A complete brief took forty minutes. The teammate picking it up would skim it anyway, miss the one constraint that mattered, and message me a question I had already answered in paragraph three. So I wrote less and kept the rest in my head. Cheaper, given the reader.&lt;/p&gt;

&lt;p&gt;Then the reader changed. Half the tickets on our board are now picked up by someone who immediately pastes them into Claude Code or Codex. And an agent is the opposite kind of reader: it will happily consume two thousand words of constraints, and it will not skim. Suddenly the forty-minute brief pays for itself.&lt;/p&gt;

&lt;p&gt;Except the person supervising the run still does not want to read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One task, two readers
&lt;/h2&gt;

&lt;p&gt;That is the actual problem, and it took me embarrassingly long to name it. A task in an agentic workflow has two participants with completely different needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The agent&lt;/strong&gt; needs the full context. Intended outcome, source material, constraints, edge cases, what evidence to produce at delivery, and the conditions under which it should stop and ask instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The human&lt;/strong&gt; needs a runbook. Why this matters, how to start the right session, which checks need human eyes, which decisions they are allowed to make alone, and where the agent must escalate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing one document for both produces something that fails both. Trim it for the human and the agent hallucinates the missing constraints. Write it for the agent and the human skims, misses the escalation rule, and rubber-stamps a bad delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually write now
&lt;/h2&gt;

&lt;p&gt;Every work item gets two blocks. Same task, same intended outcome, two audiences.&lt;/p&gt;

&lt;p&gt;The agent block is long and boring on purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Outcome: CSV export on the reports page matches the on-screen totals.
Source: apps/reports/, the export path is in exporters/csv.ts
Constraints:
- Do not change the on-screen aggregation. It is correct.
- Currency must stay in minor units end to end.
- Timezone for date bucketing is the workspace timezone, not UTC.
Edge cases: empty result set, &amp;gt;50k rows, refunds (negative amounts).
Evidence at delivery: a diff, the new tests, and one exported CSV
  for the 50k-row fixture.
STOP and ask if: the on-screen totals themselves look wrong.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The human block is five lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Why: finance is reconciling by hand every month because of this.
Start: open the repo, run the agent against the task above.
You decide: whether the refund rows belong in the export at all.
Verify: open the CSV in Sheets, check one month against the UI.
Escalate: any change to the on-screen aggregation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second block is the one that gets read. That is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I got wrong first
&lt;/h2&gt;

&lt;p&gt;My first attempt at this was two separate tickets, one "for the AI" and one "for the human". That was worse than doing nothing. The two drifted within a week: someone updated the constraint in the agent ticket, nobody touched the human one, and the reviewer verified against a rule that no longer existed.&lt;/p&gt;

&lt;p&gt;They have to be one work item with two sections, sharing one intended outcome and one definition of done. If you cannot express both from a single outcome statement, that is a signal the task is really two tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "stop and ask" earns its place
&lt;/h2&gt;

&lt;p&gt;The line I would keep if I could only keep one is &lt;code&gt;STOP and ask if:&lt;/code&gt;. Agents are relentlessly agreeable. Handed an ambiguous task, they will pick an interpretation and build something coherent on top of it, and coherent-but-wrong is far more expensive to unwind than a question asked up front.&lt;/p&gt;

&lt;p&gt;Naming the specific conditions under which the work should halt converts that failure mode into a five-minute conversation. It is the single highest-leverage line in the brief and it costs one sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification is a human column, not a checkbox
&lt;/h2&gt;

&lt;p&gt;The other thing worth being explicit about: which checks a person actually has to perform with their eyes. "Tests pass" is not verification when the agent wrote the tests. Naming one concrete manual check — open the file, compare one month, click the button — catches the class of failure where everything is internally consistent and collectively wrong.&lt;/p&gt;




&lt;p&gt;We formalised this into a pattern we call the Dual Prompt, and there is a longer write-up with the reasoning and a fuller template here: &lt;a href="https://wagglet.com/blog/dual-prompt-human-agent-task-design" rel="noopener noreferrer"&gt;https://wagglet.com/blog/dual-prompt-human-agent-task-design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want the mechanical version — a board where each item carries both blocks and the delivery loop enforces the evidence requirement — that is roughly what we built at &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt;, and the workflow docs are at &lt;a href="https://wagglet.com/docs/task-handoff" rel="noopener noreferrer"&gt;https://wagglet.com/docs/task-handoff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Curious whether other people have landed on something similar. The failure mode I still have not solved is keeping the human block genuinely short as the task grows.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Particle effects for Three.js and PixiJS without the reload loop</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Mon, 24 Aug 2026 11:48:23 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/particle-effects-for-threejs-and-pixijs-without-the-reload-loop-7ld</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/particle-effects-for-threejs-and-pixijs-without-the-reload-loop-7ld</guid>
      <description>&lt;p&gt;Most of the cost of web-game VFX isn't the effect. It's the loop you run to see the effect.&lt;/p&gt;

&lt;p&gt;Edit a config object. Rebuild the bundle. Reload the game. Get to the state where the thing actually fires. Squint. Change &lt;code&gt;emitRate&lt;/code&gt; from 240 to 180. Do it again. Forty iterations later you have a fire effect that's fine, and you've spent an afternoon.&lt;/p&gt;

&lt;p&gt;This post is about the parts of that loop you can cut, for both Three.js and PixiJS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight knobs
&lt;/h2&gt;

&lt;p&gt;Every particle system — Unity's, Unreal's, three.quarks, Pixi's emitter, whatever you hand-roll — exposes roughly the same eight things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;emitter shape and rate&lt;/strong&gt; — where particles spawn, how many per second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;lifetime&lt;/strong&gt; — how long each one lives; the clock every curve runs against&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;velocity&lt;/strong&gt; — initial speed and direction, spread across the emission shape&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;forces&lt;/strong&gt; — gravity, drag, noise; what bends the path after spawn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;color over life&lt;/strong&gt; — tint and alpha; the difference between fire and smoke is mostly this&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scale over life&lt;/strong&gt; — grow on birth, shrink on death; most of the readability lives here&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;texture&lt;/strong&gt; — soft dot, spark streak, or a flipbook&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;material / blend mode&lt;/strong&gt; — additive for energy and light, alpha-blended for smoke and debris&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Layered effects add multi-emitter timelines, trails and sub-emitters on top of the same eight. If you understand the list, you can read any effect file in any tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hand-written configs are fine, until curves
&lt;/h2&gt;

&lt;p&gt;Here's a fire burst as a plain object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;emitRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lifetime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;speed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;gravity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;sizeOverLife&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;colorOverLife&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#fff2b0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff5a1e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;blend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;additive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is precise, diffable and lives in git. Nothing wrong with it. The problem starts the moment an effect needs a non-linear curve — an alpha that holds then falls off a cliff, a size that pops then eases. You cannot feel a bezier as four numbers. You can only compile it and look.&lt;/p&gt;

&lt;p&gt;So: author the curves visually, export JSON, keep the runtime in code. The effect still ends up as a reviewable file in the repo; you just stop guessing at the numbers that go in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three.js side
&lt;/h2&gt;

&lt;p&gt;The game-side code is three calls. Load, spawn, update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;effects/explosion.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object3D&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onHit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spawnAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAnimationLoop&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDelta&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things worth getting right here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clamp your delta.&lt;/strong&gt; &lt;code&gt;clock.getDelta()&lt;/code&gt; after a tab switch can hand you two seconds. Effects then jump forward and look broken. Clamp to something like 1/20s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pool, don't allocate.&lt;/strong&gt; Construct one emitter per effect type at load and reuse it, rather than building a new one per hit. Garbage collection during combat is a visible stutter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make sure the preview is the effect.&lt;/strong&gt; If your editor and your runtime use different simulation code, the preview is an approximation and you're back to reload-and-check. A shared deterministic simulation is what actually kills the loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PixiJS side
&lt;/h2&gt;

&lt;p&gt;Same authoring, different stage. 2D world effects and UI effects use the identical JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;effects/coin-burst.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;uiLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pointertap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spawnAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ticker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deltaMS&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;UI particles are underrated. A button that sparkles on tap, a reward counter that bursts, a progress bar that throws off sparks as it fills — these cost almost nothing and change how finished a game feels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six rules for 60 FPS on a phone
&lt;/h2&gt;

&lt;p&gt;The target is a mid-range Android, not your desktop.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cap particle counts&lt;/strong&gt; per effect and per scene. Twenty well-timed particles beat two hundred.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch overdraw.&lt;/strong&gt; Large transparent quads stacked on each other are the most common frame killer — not particle count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atlas your textures.&lt;/strong&gt; One atlas, one draw call. Flipbooks belong in the same sheet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower emission, not lifetime.&lt;/strong&gt; Fewer particles reads better than shorter ones. Keep the silhouette, drop the density.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean up finished effects.&lt;/strong&gt; Return emitters to the pool on completion. Leaked emitters cost frames silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Profile the worst case on device.&lt;/strong&gt; Several effects firing at once, mid-combat, on real hardware.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Letting an agent write effects
&lt;/h2&gt;

&lt;p&gt;"Make me a fire effect" gets you plausible code with invented APIs and parameters nobody validated, and usually an effect you can't open in an editor afterwards.&lt;/p&gt;

&lt;p&gt;What works better: give the agent the actual schema and runtime API as a skill, have it write effect JSON rather than bespoke code, validate the export with a CLI, then open the result in the editor and look at it. The agent drafts; you still judge it with your eyes. VFX is one of the few things where a human has to be the acceptance test.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool
&lt;/h2&gt;

&lt;p&gt;I've been building &lt;a href="https://nixiefx.com/" rel="noopener noreferrer"&gt;NixieFX&lt;/a&gt; around this workflow: a browser particle editor with multi-emitter timelines, curves and gradients, forces and noise, flipbooks, trails, sub-emitters and a node-based material workflow. Effects export as JSON into your own project folder, the editor and runtime share one deterministic simulation, and there are open-source runtimes for both Three.js and PixiJS. There are also skills for Claude Code, Codex, Cursor, Gemini and Copilot.&lt;/p&gt;

&lt;p&gt;Free, no install, no account: &lt;a href="https://nixiefx.com/" rel="noopener noreferrer"&gt;nixiefx.com&lt;/a&gt; · &lt;a href="https://github.com/azakhary/nixie-fx" rel="noopener noreferrer"&gt;runtime on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Curious what everyone else is using for web-game VFX — three.quarks, hand-rolled shaders, something else?&lt;/p&gt;

</description>
      <category>treejs</category>
      <category>vfx</category>
      <category>javascript</category>
      <category>webgl</category>
    </item>
    <item>
      <title>One task, two readers: briefs for the agent and the human at the same time</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Thu, 20 Aug 2026 07:59:19 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/one-task-two-readers-briefs-for-the-agent-and-the-human-at-the-same-time-dn4</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/one-task-two-readers-briefs-for-the-agent-and-the-human-at-the-same-time-dn4</guid>
      <description>&lt;p&gt;I spent a long time writing deliberately vague task descriptions, and I could defend it. A complete brief took forty minutes. The teammate picking it up would skim it anyway, miss the one constraint that mattered, and message me a question I had already answered in paragraph three. So I wrote less and kept the rest in my head. Cheaper, given the reader.&lt;/p&gt;

&lt;p&gt;Then the reader changed. Half the tickets on our board are now picked up by someone who immediately pastes them into Claude Code or Codex. And an agent is the opposite kind of reader: it will happily consume two thousand words of constraints, and it will not skim. Suddenly the forty-minute brief pays for itself.&lt;/p&gt;

&lt;p&gt;Except the person supervising the run still does not want to read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One task, two readers
&lt;/h2&gt;

&lt;p&gt;That is the actual problem, and it took me embarrassingly long to name it. A task in an agentic workflow has two participants with completely different needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The agent&lt;/strong&gt; needs the full context. Intended outcome, source material, constraints, edge cases, what evidence to produce at delivery, and the conditions under which it should stop and ask instead of guessing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The human&lt;/strong&gt; needs a runbook. Why this matters, how to start the right session, which checks need human eyes, which decisions they are allowed to make alone, and where the agent must escalate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Writing one document for both produces something that fails both. Trim it for the human and the agent hallucinates the missing constraints. Write it for the agent and the human skims, misses the escalation rule, and rubber-stamps a bad delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually write now
&lt;/h2&gt;

&lt;p&gt;Every work item gets two blocks. Same task, same intended outcome, two audiences.&lt;/p&gt;

&lt;p&gt;The agent block is long and boring on purpose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Outcome: CSV export on the reports page matches the on-screen totals.
Source: apps/reports/, the export path is in exporters/csv.ts
Constraints:
- Do not change the on-screen aggregation. It is correct.
- Currency must stay in minor units end to end.
- Timezone for date bucketing is the workspace timezone, not UTC.
Edge cases: empty result set, &amp;gt;50k rows, refunds (negative amounts).
Evidence at delivery: a diff, the new tests, and one exported CSV
  for the 50k-row fixture.
STOP and ask if: the on-screen totals themselves look wrong.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The human block is five lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Why: finance is reconciling by hand every month because of this.
Start: open the repo, run the agent against the task above.
You decide: whether the refund rows belong in the export at all.
Verify: open the CSV in Sheets, check one month against the UI.
Escalate: any change to the on-screen aggregation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second block is the one that gets read. That is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I got wrong first
&lt;/h2&gt;

&lt;p&gt;My first attempt at this was two separate tickets, one "for the AI" and one "for the human". That was worse than doing nothing. The two drifted within a week: someone updated the constraint in the agent ticket, nobody touched the human one, and the reviewer verified against a rule that no longer existed.&lt;/p&gt;

&lt;p&gt;They have to be one work item with two sections, sharing one intended outcome and one definition of done. If you cannot express both from a single outcome statement, that is a signal the task is really two tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "stop and ask" earns its place
&lt;/h2&gt;

&lt;p&gt;The line I would keep if I could only keep one is &lt;code&gt;STOP and ask if:&lt;/code&gt;. Agents are relentlessly agreeable. Handed an ambiguous task, they will pick an interpretation and build something coherent on top of it, and coherent-but-wrong is far more expensive to unwind than a question asked up front.&lt;/p&gt;

&lt;p&gt;Naming the specific conditions under which the work should halt converts that failure mode into a five-minute conversation. It is the single highest-leverage line in the brief and it costs one sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification is a human column, not a checkbox
&lt;/h2&gt;

&lt;p&gt;The other thing worth being explicit about: which checks a person actually has to perform with their eyes. "Tests pass" is not verification when the agent wrote the tests. Naming one concrete manual check, open the file, compare one month, click the button, catches the class of failure where everything is internally consistent and collectively wrong.&lt;/p&gt;




&lt;p&gt;We formalised this into a pattern we call the Dual Prompt, and there is a longer write-up with the reasoning and a fuller template here: &lt;a href="https://wagglet.com/blog/dual-prompt-human-agent-task-design" rel="noopener noreferrer"&gt;https://wagglet.com/blog/dual-prompt-human-agent-task-design&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want the mechanical version, a board where each item carries both blocks and the delivery loop enforces the evidence requirement, that is roughly what we built at &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt;, and the workflow docs are at &lt;a href="https://wagglet.com/docs/task-handoff" rel="noopener noreferrer"&gt;https://wagglet.com/docs/task-handoff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Curious whether other people have landed on something similar. The failure mode I still have not solved is keeping the human block genuinely short as the task grows.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Local vs remote MCP servers: which one you actually want</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Thu, 20 Aug 2026 03:16:36 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/local-vs-remote-mcp-servers-which-one-you-actually-want-3cic</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/local-vs-remote-mcp-servers-which-one-you-actually-want-3cic</guid>
      <description>&lt;p&gt;There are two kinds of MCP server, they solve different problems, and almost nothing tells you which one you are building until you are deep enough in to have already made the wrong choice.&lt;/p&gt;

&lt;p&gt;I worked this out from a submission form. More on that below, because it turns out to be the clearest signal in the whole ecosystem and it is buried in a footnote.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two shapes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Local (stdio).&lt;/strong&gt; The server runs as a process on the user's own machine. The client — Claude Desktop, Cursor, whatever — spawns it and talks to it over stdin/stdout. It is a package the user installs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote (Streamable HTTP).&lt;/strong&gt; The server is a service you host. The client connects out to a URL with a token in an Authorization header. Nothing is installed locally.&lt;/p&gt;

&lt;p&gt;That is the entire distinction, and it determines everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually differs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Who runs the code.&lt;/strong&gt; Local: the user, on their hardware. Remote: you, on yours. This is the real decision. Everything below follows from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where secrets live.&lt;/strong&gt; Local servers read credentials from the user's own environment — their shell profile, their config file. You never see them. Remote servers require the user to hold a token &lt;em&gt;you&lt;/em&gt; issued, which means you own the entire credential lifecycle: issuing, scoping, rotating, revoking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the server can reach.&lt;/strong&gt; A local server can read the user's filesystem, hit localhost, talk to their Docker daemon. A remote server can see none of that, and should not want to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update path.&lt;/strong&gt; Remote: you deploy, everyone is on the new version immediately. Local: users run whatever version they installed, possibly forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure surface.&lt;/strong&gt; A local server fails on one machine. A remote server fails for everyone at once. Pick your poison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Local&lt;/strong&gt; if you need the user's filesystem, local processes, a local database, or hardware. Or if the data must not leave their machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote&lt;/strong&gt; if the server fronts a service you already run. If your MCP server's job is to call your own API, making users install a process that proxies to your HTTP endpoint is pure overhead — you have shipped a local wrapper around a remote call, and now you maintain both.&lt;/p&gt;

&lt;p&gt;Rule of thumb: &lt;strong&gt;if the data lives on your infrastructure, the server should be remote. If it lives on the user's machine, local.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that cost me an hour
&lt;/h2&gt;

&lt;p&gt;Anthropic runs a directory of MCP servers, and there are &lt;strong&gt;two separate submission forms&lt;/strong&gt; — one for local, one for remote. The local one is framed around Desktop Extensions, and its stated requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publicly available on GitHub&lt;/li&gt;
&lt;li&gt;MIT licensed&lt;/li&gt;
&lt;li&gt;Built with Node.js&lt;/li&gt;
&lt;li&gt;A valid &lt;code&gt;manifest.json&lt;/code&gt; with the &lt;code&gt;author&lt;/code&gt; field pointing at your GitHub profile&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;.mcpb&lt;/code&gt; file, as a &lt;strong&gt;required&lt;/strong&gt; upload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the tell. A &lt;code&gt;.mcpb&lt;/code&gt; is a packaged &lt;em&gt;local&lt;/em&gt; extension that Claude Desktop installs and runs. If your server is remote, there is no bundle to produce — and the required-file field is where you discover you are on the wrong page.&lt;/p&gt;

&lt;p&gt;The link to the remote form sits in a single line near the top of the local one, easy to skim straight past.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So: read the transport requirements before you fill anything in.&lt;/strong&gt; If a form asks for a &lt;code&gt;.mcpb&lt;/code&gt;, or names Node.js as a hard requirement, it wants a local server. A rejected submission is a worse outcome than a slower correct one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth, if you are going remote
&lt;/h2&gt;

&lt;p&gt;Remote means you own the credentials, so a few things stop being optional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope tokens narrowly.&lt;/strong&gt; A token that can only read context for a single task is a much smaller problem when it leaks than one that acts as the user's whole account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make expiry the default, not revocation.&lt;/strong&gt; "Revocable" puts the burden on someone remembering. Expiring means neglect is harmless. Design for the person who forgets, because that person is you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never let a token land in a committed file.&lt;/strong&gt; Reference an environment variable in config and export the real value from the shell profile. Once a credential reaches a remote branch, deleting the line does nothing — the value is in the history, and rotating is the only real fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I landed
&lt;/h2&gt;

&lt;p&gt;I work on &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt;, which is remote. The server fronts a shared task board, so the data is ours rather than the user's. Going local would have meant shipping a process whose only job was to proxy HTTP calls to our own API, plus a bundle to package it, plus a version-skew problem — in exchange for nothing.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://wagglet.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; cover the connection and credential model if you want a worked example of the remote side.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on Wagglet.&lt;/em&gt; The local/remote decision above is the part worth taking regardless of what you are building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short version
&lt;/h2&gt;

&lt;p&gt;Data on your infrastructure, go remote. Data on the user's machine, go local.&lt;/p&gt;

&lt;p&gt;And before you fill in any directory form, check whether it wants a &lt;code&gt;.mcpb&lt;/code&gt;. That is the fastest way to tell which door you are standing at.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>You don't need Unity to ship a browser game</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Wed, 19 Aug 2026 08:49:02 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/you-dont-need-unity-to-ship-a-browser-game-44cp</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/you-dont-need-unity-to-ship-a-browser-game-44cp</guid>
      <description>&lt;p&gt;If you are evaluating engines for a web game, the default recommendation you will get is Unity, and the default reason is "it's the industry standard." That is true and mostly irrelevant to the question you are actually asking.&lt;/p&gt;

&lt;p&gt;I have shipped web games both ways. This is the comparison I wish someone had given me — including the parts where Unity is genuinely the right answer, because there are several and pretending otherwise would make the rest of this untrustworthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The thing that decides it: are you targeting the browser, or exporting to it?&lt;/strong&gt;&lt;br&gt;
This is the whole argument, so I will put it first.&lt;/p&gt;

&lt;p&gt;Unity's WebGL support is an export target. You build in an editor designed for native platforms, then emit a browser build. The web is a destination, not the native habitat. Everything downstream of that decision — bundle size, load time, how you debug — follows from it.&lt;/p&gt;

&lt;p&gt;A web-native stack (Three.js, PixiJS, plain TypeScript) treats the browser as the primary platform. The thing you develop in is the thing users run.&lt;/p&gt;

&lt;p&gt;If the browser is one of five platforms you ship to, Unity's export model is a reasonable trade. If the browser is where your players actually are — an instant-play game, a playable ad, a web-first F2P title — you are paying the export tax on every single build for a capability you are not using.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the web stack measurably wins&lt;/strong&gt;&lt;br&gt;
Bundle size and time-to-first-frame. A Unity WebGL build ships an engine runtime before it ships your game. Your first-load payload includes a large WASM binary whether your game is a puzzler or an open world. A Three.js/PixiJS build ships roughly what you wrote plus the libraries you imported, and code-splits like any other web app.&lt;/p&gt;

&lt;p&gt;This matters disproportionately because web game players do not wait. Every second of load costs you a measurable share of your funnel, and on a mobile connection an engine-runtime-first payload is a real conversion problem. If your acquisition depends on instant play, this alone can decide the stack.&lt;/p&gt;

&lt;p&gt;Iteration speed. Web reload is sub-second. No editor domain reload, no build step between "change a number" and "see the number." Over a tuning-heavy day — which is most days on a live game — this compounds into hours.&lt;/p&gt;

&lt;p&gt;Live ops without store review. This is the one that surprises people who have not shipped F2P. An engine game ships a balance change through a binary build and store review. A web game pushes JavaScript and assets over the air in minutes. When you need to fix a broken economy on a Saturday, "minutes" and "a week" are different businesses. Wrapping with Capacitor and delivering updates over the air via Capawesome gets you native store presence and keeps the fast path.&lt;/p&gt;

&lt;p&gt;Debugging is just the browser. Chrome DevTools, real heap snapshots, a real network panel, real profiling — the same tooling the entire web industry sharpens every year. Nothing engine-specific to learn.&lt;/p&gt;

&lt;p&gt;AI agents can actually read your project. This is newer and, I think, the most underrated item. A web game is plain TypeScript in plain files, which is the format LLMs are strongest at and have seen the most of. A Unity project lives in binary scenes, serialized YAML, meta files, and editor state that an agent cannot reliably read or write. If a meaningful share of your output now comes from coding agents, project format stops being a detail and becomes a throughput ceiling. We wrote up the full web-native F2P stack and the iteration-speed argument behind it if you want the long version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where Unity genuinely wins — and you should pick it&lt;/strong&gt;&lt;br&gt;
I would be wasting your time if I did not write this section properly.&lt;/p&gt;

&lt;p&gt;Existing team expertise. If your team knows Unity, that beats every architectural argument on this page. Retraining costs more than the export tax. This is the single most common correct reason to choose Unity and it is not a cop-out.&lt;/p&gt;

&lt;p&gt;3D-heavy, near-native visual ambition. Advanced lighting, complex physics, large streamed worlds — Unity's renderer and tooling are years ahead, and Three.js will cost you significant custom engineering to approach it.&lt;/p&gt;

&lt;p&gt;The asset store and middleware ecosystem. Genuinely enormous. Need an inventory system, a dialogue tree, a specific mobile ad SDK integration? On Unity you buy it this afternoon. On the web you write it.&lt;/p&gt;

&lt;p&gt;Real multi-platform reach. Console, XR, native mobile with heavy device integration. This is not the web's fight and web advocates should stop pretending it is.&lt;/p&gt;

&lt;p&gt;Complex editor-driven content workflows. Scene composition by level designers who are not engineers is something Unity's editor does well and a code-first stack does badly by default.&lt;/p&gt;

&lt;p&gt;The honest summary: Unity is the better engine. The web is the better platform for web games. Those are compatible statements, and confusing them is what makes this debate so tedious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The gap you will hit, and where it actually is&lt;/strong&gt;&lt;br&gt;
Assume you pick the web stack. Rendering is solved — Three.js for the world, PixiJS for the UI layer on top, both mature.&lt;/p&gt;

&lt;p&gt;The gap is VFX authoring, and it is worth knowing about before you commit rather than discovering it in month three.&lt;/p&gt;

&lt;p&gt;In Unity you have a visual particle editor. Your VFX artist opens it, tunes a curve, sees the result, hands over a prefab. On a bare web stack, "authoring particle effects" often means an engineer typing numbers into a config object and reloading — which means every VFX iteration costs engineering time, and your artist is blocked on someone else's calendar. That is an organisational bottleneck disguised as a tooling gap, and it is the one thing I would call a real regression versus Unity.&lt;/p&gt;

&lt;p&gt;It is solvable, and closing it is why NixieFX exists: visual particle and material authoring in the browser, projects stored as plain JSON so a VFX change is code-reviewable like anything else, and an &lt;a href="https://nixiefx.com/threejs-runtime/" rel="noopener noreferrer"&gt;export bundle you load into Three.js&lt;/a&gt; with npm install nixie-fx three. The artist iterates and commits JSON; no engine build, no programmer in the loop. If you want the argument for why the classic engine era is ending for F2P specifically, &lt;a href="https://nixiefx.com/why-unity-unreal-era-is-over/" rel="noopener noreferrer"&gt;we made that case at length.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disclosure: I work on NixieFX. The Unity-wins section above is not false modesty — if you are building a 3D console title with a Unity-fluent team, use Unity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to actually decide&lt;/strong&gt;&lt;br&gt;
Skip the engine debate and answer three questions:&lt;/p&gt;

&lt;p&gt;Is instant play in a browser core to how players reach you? If yes, weight load time heavily, and the web stack wins on that axis alone.&lt;br&gt;
Does your team already know Unity? If yes, that is probably decisive, and that is fine.&lt;br&gt;
How often do you need to change live content? If the answer is "weekly or faster," over-the-air updates are worth more than renderer features.&lt;br&gt;
You do not need Unity to ship a browser game. You might still want it — but it should be because of question 2, not because it is the default answer.&lt;/p&gt;

&lt;p&gt;If you have shipped a web game at scale and disagree — particularly on the asset-ecosystem gap, which I think is the strongest remaining argument for Unity — I would genuinely like to hear where this framing breaks down.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gamedev</category>
      <category>nextjs</category>
      <category>gamechallenge</category>
    </item>
    <item>
      <title>AI coding agent usage limits: what actually resets, and when</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Wed, 19 Aug 2026 08:40:05 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/ai-coding-agent-usage-limits-what-actually-resets-and-when-7h4</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/ai-coding-agent-usage-limits-what-actually-resets-and-when-7h4</guid>
      <description>&lt;p&gt;AI coding agent usage limits: what actually resets, and when&lt;br&gt;
Most confusion about AI usage limits is not about the numbers. It is about the fact that every vendor meters something different, on more than one clock, and the word "reset" means at least three unrelated things depending on whose product you are using.&lt;/p&gt;

&lt;p&gt;I lost an afternoon to this. I hit a wall mid-refactor, assumed I was out until the next billing period, and shelved the work. I was actually only out on the short clock and would have been fine after a coffee.&lt;/p&gt;

&lt;p&gt;So: the general mental model that holds across tools, then the practical part — how to scope work so limits stop mattering much. No specific figures anywhere in this post, deliberately, and I will explain why.&lt;/p&gt;

&lt;p&gt;First: what is being counted?&lt;br&gt;
Before you can reason about a limit, you have to know its unit. Broadly there are three, and tools mix them:&lt;/p&gt;

&lt;p&gt;Tokens. The most common for API-billed usage. Roughly, chunks of text - both what you send and what comes back. Two things people miss: output usually costs more than input, and the entire conversation is re-sent on every turn. Which means turn 40 of a session is dramatically more expensive than turn 4, even if you typed the same number of words. More on why that matters below.&lt;/p&gt;

&lt;p&gt;Requests or messages. Some subscription tools count interactions rather than volume, sometimes labelled "premium requests" or similar. Under this model a huge task and a one-line question cost the same, which inverts your strategy completely — batching is free, and asking many small questions is what hurts.&lt;/p&gt;

&lt;p&gt;Credits. An abstraction over the other two, usually with different models drawing at different rates.&lt;/p&gt;

&lt;p&gt;The single most useful thing you can do is find out which of these your tool uses, because strategies that help under one model actively hurt under another.&lt;/p&gt;

&lt;p&gt;Second: there is usually more than one clock&lt;br&gt;
A short rolling window. Covers a few hours of heavy use, then rolls off. Critically it is rolling, not a calendar bucket — usage from early in the window ages out continuously, which is why you can sometimes come back in twenty minutes and have room. This is the limit you hit most and panic about most unnecessarily.&lt;/p&gt;

&lt;p&gt;A longer ceiling tied to a week or a billing cycle. This is the one that genuinely ends your week, and the reason a single very heavy day can quietly cost you Thursday.&lt;/p&gt;

&lt;p&gt;Per-model sub-limits. The most capable models are almost always metered harder than the cheap ones, whether by a separate cap or a higher credit rate.&lt;/p&gt;

&lt;p&gt;"Reset" can therefore mean: a rolling window aging off, a calendar period flipping, or a credit balance refilling on your billing date. Three different behaviours. Worth knowing which one you are waiting for before you decide to wait.&lt;/p&gt;

&lt;p&gt;On actual numbers: I am not quoting any. They differ by vendor and plan and they get revised constantly — anything I wrote today would be wrong by the time you found this via search. Check your vendor's own limits page, and treat any blog post quoting hard figures (including ones ranking above this) as possibly stale. The structure above is stable even as the numbers move, and you can plan against structure.&lt;/p&gt;

&lt;p&gt;Why "just wait for the reset" is worse than it looks&lt;br&gt;
The cost people underestimate is not the agent's. It is yours.&lt;/p&gt;

&lt;p&gt;The context you built over the last hour is perishable. You had three approaches in your head, rejected two for reasons you never wrote down, and had just worked out that the legacy adapter is load-bearing.&lt;/p&gt;

&lt;p&gt;Come back five hours later and that is gone. You will re-litigate a decision you already made, and possibly land on the approach you already rejected.&lt;/p&gt;

&lt;p&gt;So the real question is not "how do I get more quota." It is "how do I stop accumulating expensive undocumented context that a five-hour gap destroys."&lt;/p&gt;

&lt;p&gt;Four strategies, in the order I reach for them&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Batch the work, then get out of the loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Under token metering this is the big one, because of the re-sending problem. Twenty small conversational nudges each re-send the whole accumulated context — you pay for the same history twenty times. One well-specified task costs a fraction and produces a better audit trail.&lt;/p&gt;

&lt;p&gt;Caveat: if your tool counts requests rather than tokens, this is already free and you should ignore it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scope tasks to fit one window.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a task cannot finish inside a single short window, it is usually not one task. "Migrate the auth module" is a programme of work. "Auth should make one network call per session instead of one per request; auth_spec must still pass" is a task, and it fits.&lt;/p&gt;

&lt;p&gt;The forcing function: if you cannot state the definition of done as a command and its expected output, the task is not scoped yet. Better to find that out before spending the quota.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put cheap models on mechanical work.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since top-tier models are metered hardest everywhere, spending them on rote work is the most common way people burn a week. Renames across files, the obvious test, mechanical format translation — none of that needs your most expensive model. Save the tightly-metered capacity for work where reasoning quality changes the outcome.&lt;/p&gt;

&lt;p&gt;Biggest difference for me, costs nothing to adopt, works on every vendor.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hand the task off rather than sit on it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a teammate has capacity and the work is genuinely unblocked, the work should move. The trap is how most teams do it: pasting an API key into Slack, or letting someone log into your account. Now you have a shared secret with no scope, no expiry and no audit trail, in a DM that is still searchable in two years.&lt;/p&gt;

&lt;p&gt;The principle that fixes it: the unit you share is the task, not the account. Whoever picks it up authenticates as themselves, against their own subscription, with a credential scoped to that one task.&lt;/p&gt;

&lt;p&gt;That requires the task to be written down properly — which loops back to strategy 2. A handoff is only as good as its specification.&lt;/p&gt;

&lt;p&gt;Reach for this one last. Fix scoping first; it makes everything else smaller.&lt;/p&gt;

&lt;p&gt;The part that surprised me&lt;br&gt;
Every strategy here is good practice regardless of limits.&lt;/p&gt;

&lt;p&gt;Batching produces better diffs. Tight scoping produces checkable outcomes. Model tiering saves money on any plan. Written-down tasks are easier to resume even if nobody else touches them.&lt;/p&gt;

&lt;p&gt;So limits are not really a constraint you work around. They are a forcing function toward habits you should have had anyway. I write better task descriptions now than when I had effectively unlimited quota, purely because sloppiness got expensive.&lt;/p&gt;

&lt;p&gt;We build &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt; around the handoff half of this — documenting a task so someone else can claim it and run it on their own subscription, whichever vendor that is, with per-task scoped credentials instead of a shared account. If that is your bottleneck: &lt;a href="https://wagglet.com/blog/dual-prompt-human-agent-task-design" rel="noopener noreferrer"&gt;using unused agent capacity across a team&lt;/a&gt; and &lt;a href="https://wagglet.com/blog/dual-prompt-human-agent-task-design" rel="noopener noreferrer"&gt;writing a task two audiences can both read&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on Wagglet. The metering, scoping and model-tiering advice above is vendor-neutral and free.&lt;/p&gt;

&lt;p&gt;What does your tool actually count — tokens, requests, or credits? I am increasingly convinced most people do not know, and that it is the root of most bad quota strategy&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Hand Off AI Agent Work When You Hit Your weekly Usage Limit</title>
      <dc:creator>Sam Novak</dc:creator>
      <pubDate>Tue, 18 Aug 2026 19:19:36 +0000</pubDate>
      <link>https://dev.to/sam_novak_574b07811e18495/how-to-hand-off-ai-agent-work-when-you-hit-your-weekly-usage-limit-4294</link>
      <guid>https://dev.to/sam_novak_574b07811e18495/how-to-hand-off-ai-agent-work-when-you-hit-your-weekly-usage-limit-4294</guid>
      <description>&lt;p&gt;You are forty minutes into a refactor. The agent has read the codebase, you have corrected it twice, it finally understands that the legacy adapter is load-bearing and cannot be deleted. You are close.&lt;/p&gt;

&lt;p&gt;Then you hit your usage limit.&lt;/p&gt;

&lt;p&gt;Everyone who codes with an AI agent daily has had this moment, and the two obvious responses are both bad.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two bad options: wait, or share credentials
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wait for the reset.&lt;/strong&gt; Sometimes correct. But the context you built up is perishable — not the agent's context window, &lt;em&gt;yours&lt;/em&gt;. Come back in five hours and you have forgotten which of the three approaches you rejected and why. You will re-litigate a decision you already made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hand over your credentials.&lt;/strong&gt; Someone on your team has quota left. You paste an API key into Slack, or worse, they log into your account. Now you have a shared secret with no audit trail, no scope, and no expiry, sitting in a DM that will still be searchable in two years. Everyone knows this is wrong. Everyone does it anyway, because the alternative is losing the afternoon.&lt;/p&gt;

&lt;p&gt;There is a third option, and it is less about tooling than it sounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually breaks when you hand off AI work
&lt;/h2&gt;

&lt;p&gt;Here is the thing I got wrong for a long time: I assumed handing off AI work was a &lt;em&gt;credentials&lt;/em&gt; problem. Solve the auth, and the work flows.&lt;/p&gt;

&lt;p&gt;It isn't. I have watched handoffs fail with perfect credentials, and the failure is always the same shape — the person picking it up cannot reconstruct what the task actually is.&lt;/p&gt;

&lt;p&gt;The reason is that you never wrote it down. You were talking to an agent conversationally, steering it in small corrections, and the real specification lives in the diff between what you asked for and what you accepted. That is invisible to the next person. They see the prompt and the half-finished branch, and neither one contains the sentence "we decided not to touch the adapter."&lt;/p&gt;

&lt;p&gt;So they rediscover it. Usually by breaking it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three things every claimable task needs
&lt;/h2&gt;

&lt;p&gt;After watching enough of these go sideways, I have landed on a minimum. If a task description has these three, someone else can pick it up cold. If it is missing any of them, it will bounce back to you with questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The goal, in outcome terms.&lt;/strong&gt; Not "refactor the auth module" — that is an activity, not an outcome. "Auth module should stop making a network call per request; target is one call per session." Now there is something to check against.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The constraints, especially the non-obvious ones.&lt;/strong&gt; This is the one everybody skips, and it is where all the pain is. Every "don't touch X," every "we tried Y and it deadlocks," every "the tests in Z are flaky, ignore them." These live in your head and nowhere else. Write them down or watch someone else find them the hard way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The definition of done.&lt;/strong&gt; What command do I run? What should it output? If you cannot state this, the task is not ready to hand off — and honestly, it might not be ready for &lt;em&gt;you&lt;/em&gt; to work on either.&lt;/p&gt;

&lt;p&gt;Here is the template I use. It is deliberately short, because a template nobody fills in is worse than no template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Goal&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;outcome&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stated&lt;/span&gt; &lt;span class="na"&gt;so&lt;/span&gt; &lt;span class="na"&gt;it&lt;/span&gt; &lt;span class="na"&gt;can&lt;/span&gt; &lt;span class="na"&gt;be&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Constraints&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;thing&lt;/span&gt; &lt;span class="na"&gt;that&lt;/span&gt; &lt;span class="na"&gt;must&lt;/span&gt; &lt;span class="na"&gt;not&lt;/span&gt; &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;and&lt;/span&gt; &lt;span class="na"&gt;why&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;approach&lt;/span&gt; &lt;span class="na"&gt;already&lt;/span&gt; &lt;span class="na"&gt;tried&lt;/span&gt; &lt;span class="na"&gt;and&lt;/span&gt; &lt;span class="na"&gt;rejected&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;and&lt;/span&gt; &lt;span class="na"&gt;why&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Done when&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;command&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;and&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;output&lt;/span&gt; &lt;span class="na"&gt;you&lt;/span&gt; &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Context&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;links:&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;issue&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;failing&lt;/span&gt; &lt;span class="na"&gt;CI&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thirty seconds to fill in. Saves an hour of back-and-forth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Share the task, not the account
&lt;/h2&gt;

&lt;p&gt;The auth problem is real, it is just easier than the context problem.&lt;/p&gt;

&lt;p&gt;The principle worth internalising: &lt;strong&gt;the unit you share should be the task, not the account.&lt;/strong&gt; A teammate picking up your work should authenticate as themselves, against their own subscription, with a credential scoped to that one task and nothing else.&lt;/p&gt;

&lt;p&gt;Concretely, that means scoped and revocable tokens rather than a shared key. A read-only token pinned to a single task cannot exfiltrate your other work, and it expires on its own — which matters because revocation is a step everyone forgets.&lt;/p&gt;

&lt;p&gt;If you take one operational habit from this post, take this one: &lt;strong&gt;never inline a token in a config file that gets committed.&lt;/strong&gt; Use environment variable indirection, so the config holds a reference like &lt;code&gt;${YOUR_TOKEN_ENV_VAR}&lt;/code&gt; rather than the secret itself, and export the actual value from your shell profile, which is not in the repo.&lt;/p&gt;

&lt;p&gt;Once a credential reaches a remote branch, deleting the line does nothing — the value is in the history, and rotating is the only real fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooling: when a shared markdown file stops scaling
&lt;/h2&gt;

&lt;p&gt;You can do all of the above with a shared markdown file and some discipline. I did for a while. It works until the team is bigger than about four people, at which point "which tasks are unclaimed" becomes its own problem.&lt;/p&gt;

&lt;p&gt;If you want something purpose-built, &lt;a href="https://wagglet.com" rel="noopener noreferrer"&gt;Wagglet&lt;/a&gt; is built around exactly this split — one person documents the task, another claims it and runs it on their own Claude Code or Codex subscription, with per-task scoped credentials rather than a shared account. The &lt;a href="https://wagglet.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; cover the handoff format and the security model. There is also a &lt;a href="https://wagglet.com/skill.md" rel="noopener noreferrer"&gt;raw skill file&lt;/a&gt; if you want your agent to read the workflow directly rather than you translating it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I work on Wagglet.&lt;/em&gt; The template above is the part I would defend regardless of what you use to track it — the tooling is genuinely the easy half.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bit that surprised me
&lt;/h2&gt;

&lt;p&gt;Writing tasks this way made me better at working on them alone.&lt;/p&gt;

&lt;p&gt;If I cannot state the definition of done, I do not understand the task yet. That is worth knowing &lt;em&gt;before&lt;/em&gt; I spend an hour and my whole quota discovering it. The handoff format turned out to be a thinking tool that happens to also be a handoff format.&lt;/p&gt;

&lt;p&gt;Which means the discipline is worth adopting even if you never hand anything to anyone.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What is in your handoff template that I have missed? I am especially curious how people handle the "approaches already rejected" part — that is the one I still write badly.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
