Skip to content

Add Change Feed idempotency guidance to Rule 9.1#39

Merged
TheovanKraay merged 3 commits into
mainfrom
copilot/add-idempotency-guidance-rule-9-1
Mar 23, 2026
Merged

Add Change Feed idempotency guidance to Rule 9.1#39
TheovanKraay merged 3 commits into
mainfrom
copilot/add-idempotency-guidance-rule-9-1

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 23, 2026

Rule 9.1 documents Change Feed Processor setup but says nothing about at-least-once delivery semantics. 100% of CF+counter test runs (P02/R04, P02/R05, P03/R03, P03/R04) produced non-idempotent handlers with counter += 1 patterns, causing silent double-counting on replay.

Changes

  • rules/pattern-change-feed-materialized-views.md — Added idempotency section with incorrect/correct examples across Java, .NET, Python. Three idempotent alternatives documented:
    • Replace pattern — write absolute values, not deltas
    • Conditional write — ETag-based duplicate detection
    • Mark-and-rebuild — flag dirty + recalculate from source of truth
  • AGENTS.md — Recompiled

Key guidance added

// ❌ NON-IDEMPOTENT — at-least-once replay doubles counts
profile.setTotalGamesPlayed(profile.getTotalGamesPlayed() + 1);

// ✅ IDEMPOTENT — replace with absolute value from event
profile.setHighScore(Math.max(profile.getHighScore(), score.getScore()));
Pattern Use Case Trade-off
Replace (absolute value) High scores, max/min Non-cumulative data only
Conditional write (ETag) Any update with duplicate detection Extra read + retry on conflict
Mark-and-rebuild Counters, aggregations Higher latency, needs rebuild process
Original prompt

This section details on the original issue you should resolve

<issue_title>Java Gaming Leaderboard — Rule 9.1: No Change Feed idempotency guidance — 100% of CF + counter runs produce non-idempotent handlers (P02, P03)</issue_title>
<issue_description># Rule 9.1: No Change Feed idempotency guidance — 100% of CF + counter runs produce non-idempotent handlers (P02, P03)

Field Value
GitHub #25
Type Rule Enhancement
Target Rule pattern-change-feed-materialized-views (Rule 9.1)
Action Add idempotency semantics guidance — at-least-once delivery requires idempotent handlers
Severity HIGH
Hit Rate 100% of Change Feed + counter runs
Profiles P02/R04, P02/R05, P03/R03, P03/R04
Labels enhancement, SCOPE, agent-kit, rule:change-feed

Summary

Rule 9.1 documents Change Feed Processor setup mechanics but provides no guidance on at-least-once delivery semantics. Every CF handler that implements game counters uses non-idempotent getTotalGames() + 1 patterns, causing silent double-counting on event replay.

Evidence

All confirmed non-idempotent:

Run Pattern False Safety Claim
P02/R04 setTotalGamesPlayed(get..() + 1) None
P02/R05 setGamesPlayedThisWeek(get..() + 1) Javadoc claims "idempotent"
P03/R03 totalGames += 1 Comment: "optional"
P03/R04 setGamesPlayed(get..() + 1) × 2 paths Comment: "best-score wins makes it safe"

One safe run: P07/R04 used mark-dirty-and-rebuild (inherently idempotent).

Incorrect Pattern

private void handleChanges(List<JsonNode> changes) {
    for (JsonNode node : changes) {
        PlayerProfile profile = objectMapper.treeToValue(node, PlayerProfile.class);
        // NON-IDEMPOTENT — at-least-once replay doubles counts
        profile.totalGames += 1;
        profile.totalScore += score;
        playerRepository.save(profile);
    }
}

Correct Pattern

private void handleChanges(List<JsonNode> changes) {
    for (JsonNode node : changes) {
        GameScore score = objectMapper.treeToValue(node, GameScore.class);
        // IDEMPOTENT — replace pattern: write absolute value, not delta
        PlayerProfile profile = playerRepository.findById(score.getPlayerId()).orElseGet(PlayerProfile::new);
        profile.setHighScore(Math.max(profile.getHighScore(), score.getScore()));
        // Or: conditional write with ETag for duplicate detection
        playerRepository.save(profile);
    }
}

Impact

  • Data corruption: silent double-counting on event replay — counters drift upward over time
  • Undetectable: no runtime error; data silently becomes wrong
  • 100% failure rate: every CF+counter implementation has this bug

Recommended Fix

Add to Rule 9.1:

Change Feed delivers events at-least-once. Your handler MUST be idempotent — processing the same event twice must produce the same result. Never use counter += 1 patterns. Use idempotent alternatives:

  • Replace pattern: Write the absolute value from the event, not a delta
  • Conditional write: Use ETags to detect duplicate processing
  • Mark-and-rebuild: Flag affected records and recalculate from source of truth
    </issue_description>

<agent_instructions>Analyse how agent skills are working in this repo, and propose a draft PR based on the recommendations in the issue. </agent_instructions>

Comments on the Issue (you are @copilot in this section)

@jaydestro ## SCOPE Reclassification Update

Type: Rule Enhancement (not new rule)
Target Rule: pattern-change-feed-materialized-views (Rule 9.1)
Action: Add idempotency semantics guidance — at-least-once delivery requires idempotent handlers. Add explicit warning against counter += 1 patterns with idempotent alternatives.

Rationale: Rule 9.1 documents CF Processor setup mechanics but says nothing about delivery semantics. Every CF handler with game counters used non-idempotent increment patterns, causing silent double-counting on replay.

Hit Rate: 100% of Change Feed + counter runs (4/4 applicable runs)</comment_new>


⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.

Enhances pattern-change-feed-materialized-views rule with at-least-once
delivery semantics guidance. Adds incorrect/correct code examples showing
non-idempotent counter patterns vs idempotent alternatives (replace pattern,
conditional write with ETags, mark-and-rebuild).

Fixes #25

Co-authored-by: TheovanKraay <24420698+TheovanKraay@users.noreply.github.com>
Agent-Logs-Url: https://github.com/AzureCosmosDB/cosmosdb-agent-kit/sessions/83eccdb6-8d0e-4e7d-aef4-7efbb5ee7f32
Copilot AI changed the title [WIP] Add idempotency guidance for Change Feed processing Add Change Feed idempotency guidance to Rule 9.1 Mar 23, 2026
Copilot AI requested a review from TheovanKraay March 23, 2026 13:31
@TheovanKraay
Copy link
Copy Markdown
Contributor

@copilot we don't need to update the improvements log, that is only for changes implemented via the testing frameworks within this repo. These are separately analysed changes via SCOPE testing (a separate tool). remove the log updates.

…k iterations, not SCOPE analysis

Co-authored-by: TheovanKraay <24420698+TheovanKraay@users.noreply.github.com>
Agent-Logs-Url: https://github.com/AzureCosmosDB/cosmosdb-agent-kit/sessions/69c25094-a8d0-43bf-ad2f-3a8ad83a331f
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 23, 2026

@copilot we don't need to update the improvements log, that is only for changes implemented via the testing frameworks within this repo. These are separately analysed changes via SCOPE...

Reverted the testing-v2/IMPROVEMENTS-LOG.md changes in 31c1302. Only the rule file and recompiled AGENTS.md remain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Java Gaming Leaderboard — Rule 9.1: No Change Feed idempotency guidance — 100% of CF + counter runs produce non-idempotent handlers (P02, P03)

2 participants