ai newsMo., 26. Okt.
EilmeldungTexas sues OpenAI and Sam Altman over ChatGPT’s safety for children

The AI agent made my Xcode build green. Here are 6 ways it might have cheated

"Six common 'fixes' AI coding agents make to get a Swift build or test passing, what the real fix looks like, and a pre-commit hook that catches them." AI coding agents are very good at making a red build go green. They're less reliable at making it go green for the right reason . We build Phoenix.

dev.toMontag 08:57 · 26.10.6 Min. Lesezeit
Bild: dev.to

"Six common 'fixes' AI coding agents make to get a Swift build or test passing, what the real fix looks like, and a pre-commit hook that catches them."

AI coding agents are very good at making a red build go green. They're less reliable at making it go green for the right reason.

We build Phoenix.vu, an AI coding agent for Xcode, so we read a lot of agent-written diffs. The same handful of shortcuts comes up again and again, from every model and every tool, including the agents now built into Xcode. None of them are malicious. The agent was asked to fix an error, and these are the fastest ways to make the error disappear.

Here are six to watch for, with the real fix for each.

Capture of ' cache ' with non - sendable type ' ImageCache ' in a ' @Sendable ' closure Enter fullscreen mode Exit fullscreen mode The shortcut:

final class ImageCache: @unchecked Sendable { private var storage: [ URL: UIImage ] = [:] func image ( for url: URL ) -> UIImage? { storage [ url ] } func insert ( _ image: UIImage, for url: URL ) { storage [ url ] = image } } Enter fullscreen mode Exit fullscreen mode It compiles. It also tells the compiler "trust me, this is thread-safe" when it isn't. You still have a data race on storage. The compiler has just stopped warning you about it.

The real fix: make the shared state safe. An actor is the simplest option:

actor ImageCache { private var storage: [ URL: UIImage ] = [:] func image ( for url: URL ) -> UIImage? { storage [ url ] } func insert ( _ image: UIImage, for url: URL ) { storage [ url ] = image } } // Callers now await: let cached = await cache. image ( for: url ) Enter fullscreen mode Exit fullscreen mode @unchecked Sendable has legitimate uses, for example a type protected by a lock. But if the diff adds it without adding a lock, ask why.

Static property 'shared' is not concurrency-safe because it is nonisolated global shared mutable state Enter fullscreen mode Exit fullscreen mode The shortcut:

final class Settings { nonisolated ( unsafe ) static var shared = Settings () var theme: Theme =. system } Enter fullscreen mode Exit fullscreen mode The real fix: decide who owns this state. If it never needs to change, make it a let of a Sendable type. If the UI owns it, isolate it to the main actor:

@MainActor final class Settings { static let shared = Settings () var theme: Theme =. system } Enter fullscreen mode Exit fullscreen mode 3. Force unwraps to get past optionals The error:

Value of optional type ' URL? ' must be unwrapped to a value of type ' URL ' Enter fullscreen mode Exit fullscreen mode The shortcut:

let url = URL ( string: endpoint )! Enter fullscreen mode Exit fullscreen mode The build passes, and the app crashes the first time endpoint is malformed.

guard let url = URL ( string: endpoint ) else { throw APIError. invalidURL ( endpoint ) } Enter fullscreen mode Exit fullscreen mode 4. try? that throws away the error The error:

Call can throw but is not marked with 'try' Enter fullscreen mode Exit fullscreen mode The shortcut:

let user = try? decoder. decode ( User. self, from: data ) Enter fullscreen mode Exit fullscreen mode Now a decoding failure silently becomes nil, and your UI shows an empty profile with no clue why.

The real fix: let it throw, or handle it on purpose:

do { let user = try decoder. decode ( User. self, from: data ) show ( user ) } catch { logger. error ( "Failed to decode User: \( error. localizedDescription ) " ) showError (. couldNotLoadProfile ) } Enter fullscreen mode Exit fullscreen mode try? is fine when nil really is an acceptable result. It's a problem when it was added only to make the compiler stop complaining.

Expectation failed: (order.total → 108.0) == 110.0 Enter fullscreen mode Exit fullscreen mode The shortcut:

@Test (. disabled ( "Flaky" )) func totalIncludesTax () { let order = Order ( items: [. init ( price: 100 )], taxRate: 0.10 ) #expect(order.total == 110) } Enter fullscreen mode Exit fullscreen mode Or the test is deleted, or the expected value is changed to match the wrong output. The suite is green, and the bug is now invisible.

The real fix: fix the logic the test is checking. Then, if the test really is flaky, fix the flakiness, for example by removing the dependency on time, the network or ordering.

keyNotFound ( CodingKeys ( stringValue: "price", intValue: nil ),... ) Enter fullscreen mode Exit fullscreen mode The shortcut:

struct Product: Decodable { let id: String let name: String let price: Decimal? // was: let price: Decimal } Enter fullscreen mode Exit fullscreen mode It decodes, and now every screen that uses price has to deal with nil, even though a product without a price is probably a bug in the API response.

The real fix: find out why the key is missing. If the API really does omit it sometimes, decode it explicitly with a documented default:

init ( from decoder: Decoder ) throws { let c = try decoder. container ( keyedBy: CodingKeys. self ) id = try c. decode ( String. self, forKey:. id ) name = try c. decode ( String. self, forKey:. name ) price = try c. decodeIfPresent ( Decimal. self, forKey:. price )?? 0 // API omits price for free items } Enter fullscreen mode Exit fullscreen mode A 30-second review checklist Before you accept an agent's fix, search the diff for:

Save this as.git/hooks/pre-commit and make it executable ( chmod +x.git/hooks/pre-commit ):

#!/bin/sh # Flags common "make it compile" shortcuts in staged Swift changes. PATTERNS = '@unchecked Sendable|nonisolated\(unsafe\)|try!|as!|\.disabled\(|XCTSkip' if git diff --cached -U0 -- '*.swift' | grep '^+' | grep -E " $PATTERNS "; then echo "" echo "These added lines often hide a real problem. Review them," echo "or commit with --no-verify if they're intentional." exit 1 fi Enter fullscreen mode Exit fullscreen mode It won't catch everything, but it catches the most common cases, whoever wrote the code.

We built Phoenix around this review step. It runs the build-fix loop alongside Xcode, but every change is shown as a diff that you approve or reject before it's applied, and it snapshots each file before writing so you can undo without Git. It's pay-per-task with no subscription, and your code stays on your Mac.

If you already pay for Claude or ChatGPT, Xcode's built-in agents are a good place to start, and everything above applies to them too.

What's the worst "fix" you've seen an AI agent make to get a build passing? Share it in the comments. We'll add the best ones to the checklist.

Spotted something that behaves differently in your Xcode version? Let us know in the comments and we'll update the post.

Quelle: dev.to (dev.to). Original öffnen