<?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: Mary Khachatryan</title>
    <description>The latest articles on DEV Community by Mary Khachatryan (@mary_khachatryan_153).</description>
    <link>https://dev.to/mary_khachatryan_153</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%2F4154237%2F2ec6e363-d554-427f-9daa-f33368b0f28c.jpg</url>
      <title>DEV Community: Mary Khachatryan</title>
      <link>https://dev.to/mary_khachatryan_153</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mary_khachatryan_153"/>
    <language>en</language>
    <item>
      <title>Stop Calling an Agent Run “Done”: Model Delivery, Acceptance, Merge, and Deployment Separately</title>
      <dc:creator>Mary Khachatryan</dc:creator>
      <pubDate>Fri, 02 Oct 2026 11:24:03 +0000</pubDate>
      <link>https://dev.to/mary_khachatryan_153/stop-calling-an-agent-run-done-model-delivery-acceptance-merge-and-deployment-separately-ec3</link>
      <guid>https://dev.to/mary_khachatryan_153/stop-calling-an-agent-run-done-model-delivery-acceptance-merge-and-deployment-separately-ec3</guid>
      <description>&lt;p&gt;An agent says the task is finished. A branch exists. CI is green. The ticket moves to Done.&lt;/p&gt;

&lt;p&gt;Those four facts often arrive close together, but they are not the same fact. Treating them as one creates an avoidable ambiguity: did the agent finish an attempt, did a person accept the result, did the code land, or did users receive it?&lt;/p&gt;

&lt;p&gt;A safer workflow gives each question its own state transition, permission check, and receipt.&lt;/p&gt;

&lt;p&gt;This article builds that workflow as a small state machine. The example is intentionally implementation-level: named actors, guarded transitions, idempotent delivery, and repository evidence that cannot silently overwrite review state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with two kinds of truth
&lt;/h2&gt;

&lt;p&gt;The work tracker owns the truth about intent and review. The repository and release system own the truth about integration and deployment.&lt;/p&gt;

&lt;p&gt;Do not force them into one status field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WorkState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;draft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claimed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IntegrationState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not_applicable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unreported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;branch_reported&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pull_request_open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;merged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blocked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DeploymentState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not_applicable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not_deployed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deploying&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&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;An accepted task can still be unmerged. A merged change can still be undeployed. A non-code task can be accepted without either field applying.&lt;/p&gt;

&lt;p&gt;That is not inconsistency. It is accurate modeling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define the actors before the transitions
&lt;/h2&gt;

&lt;p&gt;A lifecycle is only useful if the system knows who may move it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;requester&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;task_owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;runner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reviewer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;repository_maintainer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;release_owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Role&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;One person may hold several roles, but the permission check should still name the role being exercised. That keeps an administrator override from looking like an ordinary peer review.&lt;/p&gt;

&lt;p&gt;For higher-risk work, require the reviewer to be different from the runner. If self-verification is allowed, record it explicitly rather than presenting it as independent acceptance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the happy path boring
&lt;/h2&gt;

&lt;p&gt;The main workflow can stay small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;draft -&amp;gt; open -&amp;gt; claimed -&amp;gt; delivered -&amp;gt; accepted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each transition answers one question:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transition&lt;/th&gt;
&lt;th&gt;Question answered&lt;/th&gt;
&lt;th&gt;Required evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;draft → open&lt;/td&gt;
&lt;td&gt;Is the task safe and specific enough to offer?&lt;/td&gt;
&lt;td&gt;Prepared brief and owner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;open → claimed&lt;/td&gt;
&lt;td&gt;Who owns this attempt?&lt;/td&gt;
&lt;td&gt;Eligible runner and attempt ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;claimed → delivered&lt;/td&gt;
&lt;td&gt;What result did the runner produce?&lt;/td&gt;
&lt;td&gt;Delivery report and artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delivered → accepted&lt;/td&gt;
&lt;td&gt;Does the result satisfy the task?&lt;/td&gt;
&lt;td&gt;Reviewer verdict&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The state machine should reject shortcuts. A runner cannot accept their own delivery under the ordinary review path. A repository webhook cannot move a work item to Accepted. A successful deployment cannot invent a missing delivery report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard every transition
&lt;/h2&gt;

&lt;p&gt;The transition function should validate both the current state and the actor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Ticket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WorkState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;ownerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;runnerId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;activeAttemptId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;revision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&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;assertCanDeliver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claimed&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only claimed work can be delivered&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runnerId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only the active runner can deliver this attempt&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeAttemptId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Claimed work must have an active attempt&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;assertCanAccept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ticket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Actor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delivered&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only delivered work can be accepted&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reviewer&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Acceptance requires reviewer permission&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runnerId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Independent acceptance requires another person&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These checks belong on the server. Hiding a button in the interface is useful feedback, not authorization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat delivery as a report, not a verdict
&lt;/h2&gt;

&lt;p&gt;Delivery means “this attempt is ready for judgment.” It does not mean the work is correct.&lt;/p&gt;

&lt;p&gt;A delivery record should be append-only and tied to the active attempt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Delivery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;ticketId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;attemptId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;runnerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;commitSha&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;operationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;&lt;code&gt;operationId&lt;/code&gt; makes retries idempotent. If a client times out after the server stores the delivery, sending the same operation again should return the original result instead of creating a second delivery.&lt;/p&gt;

&lt;p&gt;Use an expected ticket revision as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deliver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;ticketId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;expectedRevision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;operationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Return the existing result for this operationId.&lt;/span&gt;
  &lt;span class="c1"&gt;// 2. Load the ticket and compare its revision.&lt;/span&gt;
  &lt;span class="c1"&gt;// 3. Check state and runner permission.&lt;/span&gt;
  &lt;span class="c1"&gt;// 4. Insert the delivery and transition atomically.&lt;/span&gt;
  &lt;span class="c1"&gt;// 5. Return the new revision.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents a stale browser tab or delayed agent process from delivering against an attempt that has already been reopened or reassigned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support two different rework paths
&lt;/h2&gt;

&lt;p&gt;Review failure is not one situation.&lt;/p&gt;

&lt;p&gt;If the same runner should fix the result, send it back to Claimed and keep ownership. Start a new attempt or revision while preserving the previous delivery as history.&lt;/p&gt;

&lt;p&gt;If another runner should take over, reopen it to Open and clear the claim. The previous delivery remains evidence, but it is no longer the standing answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ReviewDecision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accept&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;note&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;send_back&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;feedback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reopen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not implement both choices as “move the card left.” They differ in ownership, notifications, attribution, and what context the next run needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep repository updates on a separate rail
&lt;/h2&gt;

&lt;p&gt;When a delivery reports a branch or commit, store that as a claim first. Then verify it against the repository provider.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RepositoryEvidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;reportedBranch&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;verifiedHeadSha&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;pullRequestUrl&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;mergeState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IntegrationState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;checkedAt&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A repository callback can update &lt;code&gt;mergeState&lt;/code&gt;, but it must not set &lt;code&gt;state = "accepted"&lt;/code&gt;. Likewise, accepting the work must not set &lt;code&gt;mergeState = "merged"&lt;/code&gt; unless the repository confirms it.&lt;/p&gt;

&lt;p&gt;This separation matters when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;branch protection requires another review;&lt;/li&gt;
&lt;li&gt;CI fails after the task review;&lt;/li&gt;
&lt;li&gt;the base branch advances and creates a conflict;&lt;/li&gt;
&lt;li&gt;a pull request is intentionally left open for batching;&lt;/li&gt;
&lt;li&gt;a deployment rolls back after a successful merge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The UI can present these facts together without collapsing them. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Work: Accepted
Repository: Pull request open
Deployment: Not deployed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line tells a release owner exactly what remains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record events, not just the latest values
&lt;/h2&gt;

&lt;p&gt;Current state is useful for the board. Events explain how the work arrived there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TicketEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;ticketId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;actorId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;claimed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sent_back&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reopened&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accepted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;merge_state_changed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployment_state_changed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;attemptId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;An audit log makes several important distinctions visible: a failed attempt versus a bad task description, self-verification versus independent review, and an accepted result versus a completed release.&lt;/p&gt;

&lt;p&gt;It also gives teams data they can improve without inventing a single “agent success” score.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical implementation checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping this lifecycle, verify that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every mutation checks the current state on the server.&lt;/li&gt;
&lt;li&gt;Claim and delivery are attached to an attempt ID.&lt;/li&gt;
&lt;li&gt;Mutations use an idempotency key and expected revision.&lt;/li&gt;
&lt;li&gt;Delivery stores evidence but does not imply acceptance.&lt;/li&gt;
&lt;li&gt;Send-back and reopen have different ownership behavior.&lt;/li&gt;
&lt;li&gt;Acceptance records who reviewed the work and whether review was independent.&lt;/li&gt;
&lt;li&gt;Branch, pull request, merge, and deployment states come from their actual systems.&lt;/li&gt;
&lt;li&gt;Repository and deployment updates cannot overwrite the work-review state.&lt;/li&gt;
&lt;li&gt;The event log preserves superseded deliveries and rework reasons.&lt;/li&gt;
&lt;li&gt;The interface shows the separate states together in plain language.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to compare this model with a working product lifecycle, Wagglet documents a request-to-delivery implementation for &lt;a href="https://wagglet.com/blog/wagglet-workflow-request-draft-ticket-delivery" rel="noopener noreferrer"&gt;project management software for AI agents&lt;/a&gt;. The useful idea is not a particular board layout. It is refusing to let one convenient “Done” label stand in for four different decisions.&lt;/p&gt;

&lt;p&gt;When delivery, acceptance, merge, and deployment remain separate, automation becomes safer and human review becomes easier to audit. The workflow gains a few explicit transitions, but the team loses a much larger amount of guesswork.&lt;/p&gt;




&lt;p&gt;Disclosure: I’m publishing this on behalf of the Wagglet team. AI assistance was used to structure and edit this article; I reviewed the technical content and final wording.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A Safer Way to Hand Off Game Studio Tasks Between Engineers and Artists</title>
      <dc:creator>Mary Khachatryan</dc:creator>
      <pubDate>Thu, 01 Oct 2026 09:10:21 +0000</pubDate>
      <link>https://dev.to/mary_khachatryan_153/a-safer-way-to-hand-off-game-studio-tasks-between-engineers-and-artists-2ip1</link>
      <guid>https://dev.to/mary_khachatryan_153/a-safer-way-to-hand-off-game-studio-tasks-between-engineers-and-artists-2ip1</guid>
      <description>&lt;p&gt;Small game studios often have the right expertise in the room but attached to the wrong development setup.&lt;/p&gt;

&lt;p&gt;An engineer may understand the repository, build commands, and runtime constraints. An artist may be better equipped to judge animation timing, composition, readability, and whether an effect belongs in the game. The work stalls when we treat those capabilities as inseparable.&lt;/p&gt;

&lt;p&gt;A useful &lt;strong&gt;game studio task handoff between engineer and artist&lt;/strong&gt; does not ask the artist to become an engineer or ask an AI agent to replace either person. It packages one bounded task so that each participant contributes the judgment they actually possess.&lt;/p&gt;

&lt;p&gt;This guide shows how to structure that handoff without sharing accounts, credentials, or vague responsibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclosure: I work with the team behind Wagglet. This article was prepared with AI assistance and reviewed against the current product documentation. The workflow and limitations described here should still be evaluated against your studio’s own security and review requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Start with a task that has a visible result
&lt;/h2&gt;

&lt;p&gt;Consider a hit-confirm effect for a combat prototype.&lt;/p&gt;

&lt;p&gt;The effect already runs, but the team has three concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The burst obscures the enemy silhouette.&lt;/li&gt;
&lt;li&gt;The timing feels late relative to the impact animation.&lt;/li&gt;
&lt;li&gt;The effect may be too dense on a narrow mobile viewport.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engineer knows how to check out the correct branch and launch the relevant scene. The VFX artist knows what visual hierarchy, timing, and density should look like. Neither should have to surrender their account or impersonate the other.&lt;/p&gt;

&lt;p&gt;This is a promising handoff because the result is observable, the scope is narrow, and an unsuccessful attempt can be discarded.&lt;/p&gt;

&lt;p&gt;A poor candidate would be “improve combat effects.” That leaves the agent and artist to decide where the task begins, what files may change, and when the result is acceptable.&lt;/p&gt;

&lt;p&gt;A better task is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adjust the existing hit-confirm burst in the combat sandbox so the enemy silhouette remains readable at 390×844 and 1920×1080. Do not change damage timing, animation events, or unrelated effects. Return before-and-after captures and the changed files for engineering review.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second version defines an outcome, a boundary, and evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Divide the handoff into three roles
&lt;/h2&gt;

&lt;p&gt;A safe handoff separates preparation, execution, and acceptance.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The engineer prepares the technical boundary
&lt;/h3&gt;

&lt;p&gt;The engineer identifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the repository and starting revision;&lt;/li&gt;
&lt;li&gt;the scene or test case to open;&lt;/li&gt;
&lt;li&gt;the files that are likely in scope;&lt;/li&gt;
&lt;li&gt;the command used to run the project;&lt;/li&gt;
&lt;li&gt;the behaviors that must not change;&lt;/li&gt;
&lt;li&gt;the evidence required at delivery;&lt;/li&gt;
&lt;li&gt;the conditions that should stop the work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This does not require predicting every implementation detail. It means removing ambiguity that could cause the task to expand silently.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The artist runs the visual check
&lt;/h3&gt;

&lt;p&gt;The artist uses their own authorized workspace, development tools, and agent account. Their instructions should focus on the human judgment the task needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;compare the effect at the specified viewport sizes;&lt;/li&gt;
&lt;li&gt;check whether the character silhouette remains readable;&lt;/li&gt;
&lt;li&gt;inspect the delay between contact and the visible burst;&lt;/li&gt;
&lt;li&gt;reject clipping, abrupt cuts, or unrelated visual changes;&lt;/li&gt;
&lt;li&gt;capture the specified evidence;&lt;/li&gt;
&lt;li&gt;stop if the agent proposes changes outside the named effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The artist is not being asked to approve architecture, dependency changes, or production deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The engineer accepts or rejects the delivery
&lt;/h3&gt;

&lt;p&gt;The engineer reviews the diff, runs the relevant tests, and confirms that the change remains inside the agreed boundary. Delivery is evidence that the work was attempted; it is not automatic acceptance.&lt;/p&gt;

&lt;p&gt;That distinction prevents “the demo looked fine” from becoming the only quality gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write a task contract the agent can follow
&lt;/h2&gt;

&lt;p&gt;A compact task contract makes the boundary inspectable before anyone starts:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tune the combat hit-confirm burst&lt;/span&gt;
  &lt;span class="na"&gt;starting_point&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;studio/game-client&lt;/span&gt;
    &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prepared-review-branch&lt;/span&gt;
    &lt;span class="na"&gt;scene&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CombatSandbox&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hit-confirm-burst&lt;/span&gt;

&lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allowed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Existing hit-confirm effect configuration&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Effect-local texture or timing values&lt;/span&gt;
  &lt;span class="na"&gt;prohibited&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Combat damage timing&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Animation event wiring&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Shared renderer settings&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Package upgrades&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Production deployment&lt;/span&gt;

&lt;span class="na"&gt;human_checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;viewports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;390x844&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;1920x1080&lt;/span&gt;
  &lt;span class="na"&gt;verify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Enemy silhouette remains readable&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Burst begins at the visible impact&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;No clipping at either viewport&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Other combat effects remain unchanged&lt;/span&gt;

&lt;span class="na"&gt;delivery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;List of changed files&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Before-and-after captures at both viewports&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Commands run&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Test results&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Known limitations&lt;/span&gt;

&lt;span class="na"&gt;stop_conditions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Required asset or project access is missing&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;The effect cannot be isolated from combat logic&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;A dependency or shared renderer change appears necessary&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Existing unrelated changes overlap the target files&lt;/span&gt;

&lt;span class="na"&gt;acceptance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gameplay engineer&lt;/span&gt;
  &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Delivery requires engineering review before merge&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;The contract should be short enough to inspect but specific enough to prevent improvisation.&lt;/span&gt;

&lt;span class="c1"&gt;## Keep credentials attached to people&lt;/span&gt;

&lt;span class="s"&gt;The engineer and artist should each use their own authorized identities. A handoff is not a reason to copy an API key, export a browser session, or send somebody a password.&lt;/span&gt;

&lt;span class="s"&gt;The task can transfer instructions, repository context, file paths, test commands, acceptance criteria, non-secret sample data, and evidence requirements. It should not transfer another person’s credentials.&lt;/span&gt;

&lt;span class="s"&gt;If the artist lacks repository access, the task is not ready. If the agent cannot authenticate using the artist’s authorized environment, it should stop and report the missing access. It should not weaken permissions or search for another credential.&lt;/span&gt;

&lt;span class="c1"&gt;## Give the agent stop conditions, not unlimited initiative&lt;/span&gt;

&lt;span class="s"&gt;Coding agents are good at continuing. A safe handoff also tells them when not to continue.&lt;/span&gt;

&lt;span class="s"&gt;For this example, the agent should stop if the effect is coupled to combat timing, if the working tree contains overlapping changes, or if the requested visual adjustment requires a renderer-wide modification.&lt;/span&gt;

&lt;span class="s"&gt;A useful stop report is concrete:&lt;/span&gt;

&lt;span class="s"&gt;~~~text&lt;/span&gt;
&lt;span class="s"&gt;Stopped before editing.&lt;/span&gt;

&lt;span class="s"&gt;The hit-confirm effect reads its duration from the shared combat event.&lt;/span&gt;
&lt;span class="s"&gt;Changing the effect locally would also change gameplay timing.&lt;/span&gt;

&lt;span class="na"&gt;Evidence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;effects/hit-confirm.json&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Shared timing source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;combat/impact-events.ts&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;No files changed&lt;/span&gt;

&lt;span class="na"&gt;Decision needed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;Confirm whether an engineer should separate visual duration from gameplay timing.&lt;/span&gt;
&lt;span class="s"&gt;~~~&lt;/span&gt;

&lt;span class="s"&gt;That report preserves the boundary and gives the reviewer a real decision.&lt;/span&gt;

&lt;span class="c1"&gt;## Ask the artist to produce evidence&lt;/span&gt;

&lt;span class="s"&gt;“Looks good” is difficult to review later. Require evidence that corresponds to the acceptance criteria.&lt;/span&gt;

&lt;span class="s"&gt;For the hit-confirm task, a useful delivery contains&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="s"&gt;1. Before-and-after captures at both required viewports.&lt;/span&gt;
&lt;span class="s"&gt;2. A short note identifying the frame used to judge impact timing.&lt;/span&gt;
&lt;span class="s"&gt;3. A list of changed files.&lt;/span&gt;
&lt;span class="s"&gt;4. The command used to run the sandbox.&lt;/span&gt;
&lt;span class="s"&gt;5. Any warning, unsupported setting, or approximation encountered.&lt;/span&gt;
&lt;span class="s"&gt;6. Confirmation that unrelated effects were sampled and remained unchanged.&lt;/span&gt;

&lt;span class="s"&gt;The artist’s judgment remains essential, but it becomes easier for the engineer to inspect.&lt;/span&gt;

&lt;span class="c1"&gt;## Review the change from two directions&lt;/span&gt;

&lt;span class="s"&gt;The artist asks whether the visual hierarchy is correct, the effect feels synchronized, the intended style is preserved, and important gameplay information remains readable.&lt;/span&gt;

&lt;span class="s"&gt;The engineer asks whether the change stayed inside the allowed files, preserved gameplay behavior, used valid assets and settings, passed the relevant tests, and is safe to merge.&lt;/span&gt;

&lt;span class="s"&gt;The task is accepted only when both kinds of evidence are satisfactory.&lt;/span&gt;

&lt;span class="c1"&gt;## What this workflow does not prove&lt;/span&gt;

&lt;span class="s"&gt;A successful handoff does not prove that the artist can independently maintain the rendering system. It does not establish that every visual task can move outside engineering. It also does not measure a productivity improvement by itself.&lt;/span&gt;

&lt;span class="s"&gt;The workflow is most appropriate when the missing technical skill is narrow, the human runner already possesses relevant judgment, the result is observable, the work is reversible, and qualified review is available.&lt;/span&gt;

&lt;span class="s"&gt;It is a poor fit when correctness is hidden, access is inappropriate, failure is costly, or the runner cannot recognize an important failure.&lt;/span&gt;

&lt;span class="s"&gt;The same boundary is emphasized in [Wagglet’s field note on capability-bridging task handoffs](https://wagglet.com/blog/close-team-skill-gaps-with-ai-task-handoffs): agent assistance can help a teammate complete bounded work, but it does not make that teammate interchangeable with a specialist.&lt;/span&gt;

&lt;span class="c1"&gt;## Pilot the pattern with one effect&lt;/span&gt;

&lt;span class="s"&gt;A studio does not need a company-wide policy to test this workflow.&lt;/span&gt;

&lt;span class="s"&gt;Choose one low-risk visual task. Record preparation time, clarification requests, work and review time, rework, whether the task was accepted or abandoned, which stop conditions were triggered, and what evidence was missing. Then revise the template.&lt;/span&gt;

&lt;span class="s"&gt;A good handoff makes expertise easier to apply without pretending expertise no longer matters. The engineer supplies the technical boundary. The artist supplies visual judgment. The agent helps bridge the setup and implementation gap. A separate review decides whether the result belongs in the game.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>gamedev</category>
      <category>ai</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
