<?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: Aditya Reddy</title>
    <description>The latest articles on DEV Community by Aditya Reddy (@adityareddy_dev).</description>
    <link>https://dev.to/adityareddy_dev</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%2F4158587%2F45f6422d-df7c-41a5-848a-7b0c815491c5.png</url>
      <title>DEV Community: Aditya Reddy</title>
      <link>https://dev.to/adityareddy_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adityareddy_dev"/>
    <language>en</language>
    <item>
      <title>How one Desk Studio job gets from roughs to sign-off</title>
      <dc:creator>Aditya Reddy</dc:creator>
      <pubDate>Tue, 20 Oct 2026 04:43:48 +0000</pubDate>
      <link>https://dev.to/adityareddy_dev/how-one-desk-studio-job-gets-from-roughs-to-sign-off-4h22</link>
      <guid>https://dev.to/adityareddy_dev/how-one-desk-studio-job-gets-from-roughs-to-sign-off-4h22</guid>
      <description>&lt;p&gt;The awkward part of a design job is deciding whether it's done. A file exists, the headline fits, and it's easy to accept it cause opening another round means deciding what to ask for. I built Desk Studio around that decision. I wanted the roughs and the reason for the pick to stay visible, and I wanted a failed check to send the file back with something specific to fix.&lt;/p&gt;

&lt;p&gt;Here's how that worked on the studio's own launch card. The &lt;a href="https://github.com/adityareddy-dev/deskstudio/blob/4f0affa6b0f004ac7a15bdd6102e52ce7695500c/evals/golden/social-card/desk-studio-launch/state.json" rel="noopener noreferrer"&gt;saved job state&lt;/a&gt; has the pick, each round's proof result and scores, and the cost by tier. The &lt;a href="https://github.com/adityareddy-dev/deskstudio/blob/4f0affa6b0f004ac7a15bdd6102e52ce7695500c/evals/golden/social-card/desk-studio-launch/golden.json" rel="noopener noreferrer"&gt;golden record&lt;/a&gt; keeps the final result. It finished at 8.9 over three polish rounds, costing $1.34 at list price. Those are this job's results, not a promise about the next one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7by1opjqla52lm0bfdhq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7by1opjqla52lm0bfdhq.gif" alt="A team of desk robots passes one job along: three roughs, the pick, polish, proof and the sign-off" width="600" height="338"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The whole job as a cartoon: three roughs on Sonnet, the pick on Opus, polish on Haiku, proof with no model, the sign-off on Opus, then every signed-off piece back to you.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Give the pick something to choose from
&lt;/h2&gt;

&lt;p&gt;The launch card started with three directions: editorial split, type only, and frame. These were separate roughs made by the UI Designer at the senior tier. The point of a rough is to expose a layout decision while changing direction is still cheap. If everything already looks finished, it's tempting to spend the next round adjusting details in a direction that never deserved the pick.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw1106enq8uzzolm3a3lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw1106enq8uzzolm3a3lc.png" alt="Rough 1, editorial split" width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi14zsoyb07n05g5wmc7n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi14zsoyb07n05g5wmc7n.png" alt="Rough 2, type only" width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxo4hg8wz2i40qqfxq3ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxo4hg8wz2i40qqfxq3ui.png" alt="Rough 3, frame" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Art Director chose rough 2, the type-only direction. The saved reason is useful: the headline read first at both sizes, the small lamp was the only amber element, and the fixes were type sizes rather than layout. That gives the builder a boundary. Keep the structure that earned the pick and work on the type.&lt;/p&gt;

&lt;p&gt;The card had LinkedIn and Instagram post sizes. A direction had to work across both, so the pick was made from renders. Reading the source wouldn't show whether the headline actually won attention at each size.&lt;/p&gt;
&lt;h2&gt;
  
  
  Put the revisions where they're cheap
&lt;/h2&gt;

&lt;p&gt;After the pick, the UI Designer moved to the builder tier for polish. The launch job used Sonnet for roughs, Haiku for the revisions, and Opus for the pick and sign-offs. Those assignments are in the state alongside the completed desk runs.&lt;/p&gt;

&lt;p&gt;I want the expensive judgment to arrive with a concrete job to do. The builder already has a chosen direction, the notes and a screenshot. It can spend its round revising that file, then hand back something the next check can inspect. Starting from an empty prompt again would lose the decision that was just made.&lt;/p&gt;

&lt;p&gt;This also makes the cost breakdown interesting. The job's director work cost $0.81, rounded, for the pick and sign-offs. Senior work was $0.43 and builder work $0.10. The underlying total is $1.3354, which rounds to $1.34. These are recorded list-price costs, not a subscription bill or a measured saving against hiring someone. Most of this particular job's cost went into choosing and reviewing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Proof gets the rendered file
&lt;/h2&gt;

&lt;p&gt;Each polished file goes through proof before the Creative Director scores it. Proof checks the rendered pixels for text contrast, along with checks for overflow, clipping, safe zones, palette, type scale and required copy. There's no model deciding whether to waive a mechanical failure. A failure goes back to the builder with the screenshot.&lt;/p&gt;

&lt;p&gt;The launch card passed proof in every round. To see an actual rejection, look at the separate &lt;a href="https://github.com/adityareddy-dev/deskstudio/blob/4f0affa6b0f004ac7a15bdd6102e52ce7695500c/evals/golden/social-card/desk-studio-floor/state.json" rel="noopener noreferrer"&gt;dashboard demo card's state&lt;/a&gt;. Its first polish failed a safe-zone check: “deskstudio.ai” sat 32px from the top edge on LinkedIn, while the safe zone started at 38px. The state keeps that note and the failed proof result. The following round passed proof and proceeded to sign-off.&lt;/p&gt;

&lt;p&gt;That's a small error, though it's exactly the sort of thing I want a check to catch before asking for a judgment on composition. The text being present isn't enough. Where it lands in the rendered output matters. Keeping the failed result also means the final screenshot doesn't erase the work needed to reach it.&lt;/p&gt;
&lt;h2&gt;
  
  
  An average can still fail
&lt;/h2&gt;

&lt;p&gt;The Creative Director scores hierarchy, typography, spacing, colour, composition, originality, brand fit and copy. The &lt;a href="https://github.com/adityareddy-dev/deskstudio/blob/4f0affa6b0f004ac7a15bdd6102e52ce7695500c/skills/desk/rubric.md" rel="noopener noreferrer"&gt;visual rubric&lt;/a&gt; requires every axis to reach 8 and the average to reach 8.5. These are the studio's own evaluations. They aren't independent ratings of design quality.&lt;/p&gt;

&lt;p&gt;The launch card shows why both conditions matter. Its first round averaged 8.4, with originality at 7. The next round reached an average of 8.5, but originality was still 7, so it failed again. An average alone would have accepted that second round.&lt;/p&gt;

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

&lt;p&gt;The third round passed at 8.9. Originality reached 8 and the other axes were 9. That is a more useful record than a final score on its own, cause it shows which weakness kept the job open. It also keeps proof separate from taste: every launch-card round passed the mechanical checks, and the first two still failed the design review.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try a job you can inspect
&lt;/h2&gt;

&lt;p&gt;In Claude Code, install the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/plugin &lt;span class="nb"&gt;install &lt;/span&gt;desk &lt;span class="nt"&gt;--marketplace&lt;/span&gt; adityareddy-dev/deskstudio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Cursor, Codex, Gemini CLI, Copilot, Windsurf or OpenCode, add it to the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx deskstudio add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask your coding agent for a design in plain words. The installer adds the desk briefs and an instruction block so the agent can run the job. The &lt;a href="https://deskstudio.ai" rel="noopener noreferrer"&gt;gallery&lt;/a&gt; links the outputs, and the &lt;a href="https://github.com/adityareddy-dev/deskstudio" rel="noopener noreferrer"&gt;public repository&lt;/a&gt; retains the records used here.&lt;/p&gt;

&lt;p&gt;The API-key paths remain unverified end to end. I wouldn't infer that they work just cause the adapters exist. For a first job, use an output you can judge yourself, read the pick reason, and check the weakest axis against the rendered file before accepting the sign-off.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why the OpenTelemetry browser SDK wouldn't start on Safari 17</title>
      <dc:creator>Aditya Reddy</dc:creator>
      <pubDate>Fri, 02 Oct 2026 22:23:11 +0000</pubDate>
      <link>https://dev.to/adityareddy_dev/why-the-opentelemetry-browser-sdk-wouldnt-start-on-safari-17-55j5</link>
      <guid>https://dev.to/adityareddy_dev/why-the-opentelemetry-browser-sdk-wouldnt-start-on-safari-17-55j5</guid>
      <description>&lt;p&gt;On September 30 someone filed an issue on the OpenTelemetry browser SDK. &lt;code&gt;startBrowserSdk&lt;/code&gt; was throwing &lt;code&gt;TypeError: URL.parse is not a function&lt;/code&gt; on older browsers, so the SDK never started there at all.&lt;/p&gt;

&lt;p&gt;Nothing caught the error, so an app that started the SDK on load (the issue's example was while rendering a React provider) went down with it. Blank page, in production builds. The issue came with a clean reproduction too (Chromium 125 failed, Chromium 127 started fine), which made it easy to pick up.&lt;/p&gt;

&lt;p&gt;I went looking and it was one line. The SDK checks the export URL every time it starts, and it did that with &lt;code&gt;URL.parse&lt;/code&gt;:&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;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problem is, &lt;code&gt;URL.parse&lt;/code&gt; is new. Safari only got it in version 18, Chrome and Firefox in 126, all in 2024. Anyone on Safari 17, which includes iPhones still on iOS 17, or on a Chrome or Firefox older than 126 doesn't have it. The older way, &lt;code&gt;new URL(url)&lt;/code&gt;, has worked everywhere for years. It throws on a bad URL instead of returning &lt;code&gt;null&lt;/code&gt; though, which is a big part of why &lt;code&gt;URL.parse&lt;/code&gt; was added in the first place.&lt;/p&gt;

&lt;p&gt;So the fix goes back to the try/catch:&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;let&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Not `URL.parse`, which Safari &amp;lt; 18 and Chrome/Firefox &amp;lt; 126 lack.&lt;/span&gt;
  &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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 URL that doesn't parse, or isn't http or https, still returns &lt;code&gt;null&lt;/code&gt; and logs an error like before, and the SDK still refuses to start on it. Other parts of the repo already parsed URLs this way, so it wasn't a new pattern for anyone reviewing it.&lt;/p&gt;

&lt;p&gt;Testing was the annoying part, cause I didn't have an old Safari lying around. What I did instead was delete &lt;code&gt;URL.parse&lt;/code&gt; in current Chrome and start the SDK:&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;const&lt;/span&gt; &lt;span class="nx"&gt;urlParse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOwnPropertyDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;Reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;browserSdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;startBrowserSdk&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* short batch delay for the test */&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="c1"&gt;// emit a log and a span, then wait for the export&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&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;urlParse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;defineProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;urlParse&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;That test failed on main with the exact error from the issue and passed with the fix. I never ran it on a real Safari 17 or Chrome 125, and I said so in the PR.&lt;/p&gt;

&lt;p&gt;The bit I keep thinking about is why lint didn't catch it. The repo runs eslint-plugin-baseline-js set to &lt;code&gt;available: 'widely'&lt;/code&gt;, which is there to stop exactly this kind of thing. It let &lt;code&gt;URL.parse&lt;/code&gt; through cause its data counts &lt;code&gt;URL.parse&lt;/code&gt; as part of the older &lt;code&gt;url&lt;/code&gt; feature, and that one has been widely available for a long time. So a method Safari has had for about two years passed the check. I left the lint config alone, it felt like its own conversation.&lt;/p&gt;

&lt;p&gt;David Luna merged it on October 2.&lt;/p&gt;

&lt;p&gt;PR: &lt;a href="https://github.com/open-telemetry/opentelemetry-browser/pull/447" rel="noopener noreferrer"&gt;https://github.com/open-telemetry/opentelemetry-browser/pull/447&lt;/a&gt;&lt;br&gt;
Issue: &lt;a href="https://github.com/open-telemetry/opentelemetry-browser/issues/445" rel="noopener noreferrer"&gt;https://github.com/open-telemetry/opentelemetry-browser/issues/445&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>opentelemetry</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
