Index

Implementation Without Understanding Is Borrowed Code

The webhook handler passes its normal-delivery test. It passes its duplicate test too.

Then the account transition fails after the handler records the event receipt. The provider retries. The handler finds the receipt, returns duplicate, and never applies the credit.

The first response said “try again.” The durable state made trying again useless.

This is the awkward boundary in AI-assisted implementation. A generated patch can be small, readable, and green while neither its author nor reviewer has made the important failure model explicit. The problem is not that AI can write this defect. A human can write it before coffee. The problem is accepting code nobody present can predict when the happy path stops.

Implementation becomes owned engineering when the developer can state the contract, predict the important failure states, reconcile the explanation with the diff, and verify those predictions independently.

Two green tests leave the transaction unanswered

The AI-02 fixture gives two synthetic patches the same task:

Accept a payment.captured webhook once, apply a credit to an account, and return the same accepted outcome when the provider retries the event.

The answer-first patch checks the event identity, records a receipt, and then applies the credit:

$classification = $store->classify($event);

if ($classification === 'duplicate') {
    return 'duplicate';
}

$store->recordReceipt($event);
$store->applyCredit($event['account_id'], $event['amount']);

Normal delivery produces one receipt and one credit. An exact duplicate produces no additional credit. Those are useful controls. They do not answer what happens between the last two statements.

The fixture injects a failure immediately before the credit mutation. The observed sequence is:

first response:  retryable
receipt:        present
credit:         0
retry response: duplicate
credit:         0

The patch has confused “seen” with “completed.” Idempotency prevents repeated effects; it must not prevent the first effect from ever completing.

Stripe’s webhook guidance warns that endpoints may receive the same event more than once and recommends asynchronous handling after a quick successful response. That guidance establishes real delivery pressures. It does not choose this fixture’s transaction boundary. The local contract still has to say what “processed” means and what a retry is allowed to repair.

Predict the state after the exception

A review comment such as “looks idempotent” is too compressed to inspect. A larger test suite can be equally unhelpful if every assertion stops at the response or exception.

Before running either patch, the fixture declares five scenarios:

ScenarioFirst resultState after first resultRetry consequence
First deliveryacceptedreceipt and one creditduplicate, still one credit
Exact duplicateacceptedreceipt and one creditduplicate, still one credit
Transition failureretryableneither state changeretry applies one credit
Receipt failureretryableneither state changeretry applies one credit
Conflicting payloadrefusedoriginal state unchangedoperator-visible conflict

The response is only one column. The defect lives in the other two.

That table also changes the review question. “Does it catch the exception?” is easy to answer from syntax. “Which state remains, and what will the provider do next?” requires a model of the whole owned boundary.

PHP’s Throwable contract explains what the handler can catch. It does not provide rollback. Catching the injected exception and returning retryable is correct only if the state still makes a retry useful.

This follows the rule in Choose the Test Boundary from the Defect. If the feared defect is a durable receipt without a credit, a test which observes only an HTTP result is on the wrong side of the failure.

The explanation has to match the patch

The answer-first review record says the receipt and credit share one transaction. The source contains no transaction call.

That disagreement is evidence. The review is rejected even before the failure case runs.

Generated explanations are useful because they expose claims which can be compared with the diff. They become dangerous when fluency is treated as a substitute for that comparison. “Atomic,” “idempotent,” and “transactional” are not properties a paragraph can confer on code.

The fixture makes eight questions answerable from the contract and patch:

  1. Which state changes must commit together?
  2. Where can each injected failure occur?
  3. What state remains after each failure?
  4. What does the provider observe and do next?
  5. Which test detects reversed or separated write order?
  6. What property do the normal and duplicate tests not prove?
  7. What signal or repair path would a non-atomic design require?
  8. Which changed assumption would reverse the design choice?

These structured answers do not measure a real person’s comprehension. They make a minimum review model inspectable. Someone can still memorize the right words. The stronger evidence is whether the prediction was recorded before execution, agrees with the patch, and survives an injected defect.

This is why “ask the assistant to explain its code” is an incomplete protocol. The explanation must name statements in the actual diff, and an independent observation must be capable of proving it wrong.

Atomicity is the local answer, not the universal one

The second synthetic patch puts classification, receipt creation, and credit inside one transaction:

return $store->transaction(function () use ($event): string {
    $classification = $store->classify($event);

    if ($classification === 'duplicate') {
        return 'duplicate';
    }

    $store->recordReceipt($event);
    $store->applyCredit($event['account_id'], $event['amount']);

    return 'accepted';
});

When either write fails, the in-memory store restores both snapshots. The retry is classified as new and applies the credit once. All five declared scenarios pass.

The PostgreSQL transaction tutorial describes the corresponding database property: grouped steps complete as an all-or-nothing operation, with ROLLBACK discarding updates. A real implementation would also need a unique constraint on event identity and transaction behavior proven against its database, not this in-memory imitation.

The atomic patch is selected only because both synthetic state changes share one transactional resource and the response may wait for the transition. If either fact changes, the design must change with it.

An inbox with asynchronous processing is credible when the endpoint must acknowledge before business work finishes. That creates separate accepted and completed contracts, queue age, terminal failure, and repair ownership. A provider-side idempotency feature can reduce duplicate requests but cannot prove local state is complete. When receipt and business state have different authorities, compensation or reconciliation may be more honest than an atomic transaction which cannot exist.

Those alternatives are not footnotes. They are the conditions which prevent a small fixture from becoming a cargo-cult rule: “always put webhook work in one database transaction.”

A safer protocol makes ownership visible

The fixture retains an eight-gate assistance protocol:

  1. The developer states the behavior, constraints, and unknowns.
  2. The assistant proposes the smallest patch and names its assumptions.
  3. The developer predicts the failure table before execution.
  4. Tests observe the response, remaining state, and retry consequence.
  5. At least one semantic defect is injected and must be detected.
  6. The explanation is reconciled with the actual diff and results.
  7. Unresolved questions remain explicit.
  8. Acceptance names a human owner and evidence, not a confidence score.

The order matters. Writing the prediction after seeing the output records an explanation, not a prediction. Asking the same assistant to implement, explain, and approve the patch keeps the entire loop inside one candidate generator.

The protocol is deliberately heavier than accepting a three-line patch. It should be scaled to risk. A local formatting change does not need a failure table. A payment transition whose retry semantics can lose or duplicate value does.

Nor does every AI-assisted change need a custom mutation harness. In this fixture, thirty-two semantic mutations verify that the evidence contract rejects missing failure state, green controls described as complete, an explanation which invents a transaction, absent retry consequences, erased unknowns, candidate authority, and synthetic evidence attributed to Brian. The useful principle is smaller: prove that a relevant wrong implementation can fail the review and test boundary you chose.

The reproduction does not measure productivity, review speed, developer comprehension, model quality, or incident rates. It does not describe Brian’s system, provider, code, team, or preference. Its candidates and owner are synthetic, and neither candidate came from a named AI model.

It retains ten candidate-scenario results, eight review questions, thirty-two rejected semantic mutations, three authoritative source responses, twenty-six source hashes, a dependency-free PHP 8.4.12 runtime, and a clean export of commit 3e448bb2a0902dc8be3405d662ea0b808aa99582.

A generated patch stops being borrowed code when the developer can predict the failure, show where the patch enforces the contract, and recognize the evidence which would make a different design necessary. Until then, the repository owns the bytes. Nobody owns the behavior.