About DavidHLPL · RHINE LAB RSS ↗
FILE X-1075 / Project practice

UltiCode Generation and attemptId: Fencing Stale Judging Results with Conditional Updates

ENGINEERING NOTES

Published / DavidHLPL

Evidence status: This article is based on UltiCode’s Submission mapper, rejudge service, lease reaper, and Judge attempt executor. It describes a result fence implemented in the repository; it does not expand source evidence into an exactly-once or production-concurrency proof across systems.

In asynchronous judging, a submission can be executed more than once: an administrator may rejudge it, the original Worker may time out, a lease may be reclaimed, or Redis may redeliver an old message. If result writes locate records only by submissionId, the oldest result to arrive last can overwrite the newest result.

UltiCode represents the logical submission generation and the concrete execution lease separately:

  • generation: the logical epoch of a submission or rejudge;
  • attemptId: the token for one Worker execution lease.

Put concurrency rules into SQL conditions

The critical path can be reduced to four conditions:

acquire lease:  id + status=Pending + generation
renew lease:    id + current_attempt_id
write verdict:  id + generation + current_attempt_id
rejudge/reaper: expected generation -> generation + 1

In SubmissionMapper, these become conditional updates:

  1. A Worker acquires a lease only when the submission is Pending and the generation matches.
  2. A heartbeat renews the lease only while the current attemptId still owns it.
  3. A verdict is written only when id, generation, and attemptId all match.
  4. A rejudge or lease recovery first performs a CAS against the expected generation, then advances to the next generation and creates a new judging intent.

When the update affects zero rows, the caller interprets it as a lost lease, stale generation, or takeover by a competitor, rather than continuing to write an old result. The judge executor records judge.stale_result.dropped and ACKs messages already identified as safely stale.

How rejudges and reaping advance the epoch

SubmissionRejudgeService reopens a terminal submission by incrementing its current generation and creating a new outbox record. JudgingLeaseReaper uses the same progression to recover expired leases.

Therefore, even if an old message reappears in the Redis PEL, it carries an old generation. It can be consumed, identified, and safely discarded, but it cannot pass the Submission owner’s database gate.

This is a boundary, not magic

A result fence protects only the write seam it covers. It does not replace message durability, cross-owner authorization, business auditing, or idempotency in external systems. It also does not guarantee that old code will never execute; it guarantees that an old execution result has no authority to overwrite new state.

That is the value of conditional updates: concurrency correctness is narrowed from “every caller must be careful” to one database rule that can be inspected.

Applying the model to LLM/Agent systems

An Agent task can also be retried, resumed after cancellation, switched to another model, or replanned. The plan version can serve as generation, a provider lease as attemptId, and the final write can be constrained by taskId + planVersion + attemptId. A late tool result then remains an observable stale result instead of rewriting the current plan.

Minimal verification path

Read SubmissionMapper.java, then inspect the acquire/renew/write/ACK-NACK path in DefaultJudgeAttemptExecutor.java. The corresponding integration behavior should be verified with real MySQL, Redis, and concurrency tests; this article does not claim that verification was run in this round.

Research notes

  1. Original document: src/content/note/en/ulticode-generation-attempt-fence.md
  2. Source revision: f49953fb0d4bd52a39bf81167b0c4b7dd7d78a64
  3. Evidence status: provisional

Original source ↗