concurrency-and-hidden-bugs
Skill1 starsChanged 4 months agoMITView on GitHub
Use this skill whenever writing, modifying, reviewing, or planning any code that touches shared state, async operations, or anything that runs more than once at the same time. Triggers include Firebase writes, localStorage updates, counters, ID generation, multi-user/multi-branch/multi-tab features, real-time sync, async/await chains, event listeners, optimistic UI updates, or Google Apps Script Sheets writes. Also triggers on phrases like "add feature to", "fix bug in", "review my code", "audit", "refactor", "why does this happen sometimes", "works locally but not in production", "order ID duplicated", "data overwritten", "lost updates", "stale data", "race condition", "concurrency", "deadlock", "sync issue", "intermittent bug". CRITICAL: AI agents notoriously skip concurrency analysis — this skill must trigger even when the user describes a feature in non-technical terms (e.g., "let two branches order at the same time" implicitly requires concurrency review).
In this repository9 other files it gives its agents.
- AGENTS.mdAGENTS.md
- template/AGENTS.mdAGENTS.md
- debugging-disciplineSkill
- backing-up-solo-projectsSkill
- complying-with-thai-pdpaSkill
- preparing-mobile-store-submissionSkill
- verifying-ai-outputSkill
- writing-feature-specsSkill
- verifying-ai-outputSkill
The file
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 10 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈10
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 29
If unsure → default to PRE-FLIGHT.
- Distinctive: line 35
Before writing ANY code that touches shared state, answer these 6 questions out loud.
- Distinctive: line 41
If the answer is "just one actor ever" → safe to proceed.
- Distinctive: line 42
If the answer is "2+ actors" → continue to Q2.
- Distinctive: line 60
If the answer involves "both X and Y" → the bug is here.
- Very specific: line 64
If yes → must use an atomic operation (Firebase transaction() / increment(), Sheets LOCK, or a single-writer pattern).
- Distinctive: line 68
When triggered in pre-flight mode, respond with:
- Distinctive: line 87
Only after the user confirms → write code.
- Distinctive: line 233
ทำเกิน (over-engineering): Add locks everywhere instead of identifying real concurrent paths.
- Distinctive: line 253
localStorage: No atomic op exists. Use Firebase as source of truth, localStorage as cache only.
- Distinctive: line 259
If the user describes a feature and you find yourself thinking "this probably won't have concurrency issues" — that is the signal to run …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Concurrency & Hidden Bugs Guard
A guard skill that forces explicit concurrency and hidden-bug analysis before writing code, and a checklist for auditing existing code.
This skill exists because AI coding agents — including me — tend to:
- ✅ Read code line-by-line and miss timing-dependent bugs
- ✅ Generate code that "looks right" but breaks when 2+ actors hit it simultaneously
- ✅ Skip the "what if the network drops mid-write?" question
- ✅ Forget that localStorage / IndexedDB / Firebase don't enforce serialization the way a database does
This skill stops that. It has two modes.
When to use which mode
| Situation | Mode | Output |
| About to write a new feature | PRE-FLIGHT | 6 questions answered before any code is written |
| Reviewing existing code | AUDIT | Scan against 13 patterns, output risk list |
| Bug is intermittent / "sometimes works" | AUDIT + PRE-FLIGHT for the fix | Identify the race, design fix with pre-flight |
| User says "add feature X" without context | PRE-FLIGHT first, do not write code yet | Question list |
If unsure → default to PRE-FLIGHT. Cheaper to ask than to ship a race condition.
MODE 1 — PRE-FLIGHT (before implementing)
Before writing ANY code that touches shared state, answer these 6 questions out loud. If any answer is "I don't know" or "not handled", stop and design first. Do not write code.
The 6 Pre-Flight Questions
Q1. Who else can write to this state at the same time?
- Other users? Other branches? Other tabs of the same user? A scheduled job? An admin override?
- If the answer is "just one actor ever" → safe to proceed.
- If the answer is "2+ actors" → continue to Q2.
Q2. What happens if two writes arrive in the same millisecond?
- Last-write-wins? Lost update? Duplicate ID?
- Required answer: name the strategy (transaction, queue, lock, CRDT, last-write-wins-and-that's-acceptable).
Q3. What happens if the operation fails halfway through?
- Network drops after write 1 but before write 2.
- User closes the tab during async op.
- Required answer: name the rollback or recovery strategy. "It won't fail" is NOT acceptable.
Q4. What happens if the user is offline, then comes back online?
- For Firebase RTDB / Firestore: offline writes queue locally then sync. What if conflict?
- For localStorage: nothing syncs — is that OK?
- Required answer: explicit offline behavior.
Q5. What's the single source of truth for this state?
- localStorage? Firebase node? Google Sheet cell?
- If the answer involves "both X and Y" → the bug is here. Pick one.
Q6. Is there a counter, ID, or sequential field involved?
- Order IDs, invoice numbers, queue positions, version numbers.
- If yes → must use an atomic operation (Firebase
transaction()/increment(), SheetsLOCK, or a single-writer pattern). Client-sidelastId + 1is ALWAYS wrong with 2+ actors.
Pre-flight output format
When triggered in pre-flight mode, respond with:
PRE-FLIGHT for: <feature name>
Q1. Concurrent writers: <answer>
Q2. Same-millisecond strategy: <answer>
Q3. Mid-operation failure: <answer>
Q4. Offline → online: <answer>
Q5. Single source of truth: <answer>
Q6. Counter/ID involved: <answer> [+ atomic strategy if yes]
RISKS IDENTIFIED:
- <risk 1>
- <risk 2>
PROCEED? <yes / no / need more info>Only after the user confirms → write code.
MODE 2 — AUDIT (review existing code)
Scan the code against these 13 patterns. For each match, output: location + risk + suggested fix.
Category A: Concurrency
A1. Read-modify-write without atomicity
- Pattern:
const x = await read(); x.count++; await write(x); - Risk: Two concurrent operations both read the same value, both write +1 → ends at +1 not +2 (lost update).
- Fix: Use Firebase
transaction(),increment(), orserverTimestamp(). For Apps Script useLockService.
A2. Client-side ID generation (lastId + 1)
- Pattern:
const newId = orders.length + 1orconst newId = lastOrderId + 1 - Risk: Two clients see the same
lastOrderId→ duplicate IDs. - Fix: Firebase
push()(auto-ID) ortransaction()on the counter node. Never trust client.
A3. Concurrent writes to same key
- Pattern: Two code paths can
set()the same Firebase node or localStorage key. - Risk: Last-write-wins silently overwrites the other.
- Fix: Use
update()with field-level paths, or merge logic, or a queue.
A4. Multi-tab localStorage race
- Pattern: Single-file PWA where user might open 2 tabs.
- Risk: Tab A reads, Tab B writes, Tab A writes back with stale data.
- Fix: Use
storageevent listener to invalidate cache, or use BroadcastChannel, or warn against multi-tab.
Category B: State / Ordering
A5. Missing await in async chain
- Pattern:
function save() { writeToFirebase(); updateUI(); }(no await on writeToFirebase) - Risk: UI updates before write commits; on error, UI shows success.
- Fix:
await writeToFirebase()beforeupdateUI(). Or chain with.then().
A6. Promise.all when sequential is required
- Pattern:
await Promise.all([writeA(), writeB()])where B depends on A's result. - Risk: Operations run concurrently when one depends on the other → null reference or wrong state.
- Fix: Sequential
await writeA(); await writeB();.
A7. Stale closure capturing old state
- Pattern:
setTimeout(() => save(orderData), 1000)whereorderDatais mutated before the timeout fires. - Risk: Saves stale snapshot, not current state.
- Fix: Pass primitive values, not object references. Or read state fresh inside the callback.
Category C: Partial Failure
A8. Firebase write without .catch() or error handler
- Pattern:
firebase.set(data)with no error handling. - Risk: Write fails silently, UI shows success, data is lost.
- Fix: Always
.then().catch()ortry/await/catch. (This is one of Tony's non-negotiable rules.)
A9. Optimistic UI with no rollback
- Pattern: Update UI first, then write to server. No rollback on failure.
- Risk: UI shows state that doesn't exist on server. User trusts a lie.
- Fix: Roll back UI on
.catch(). Or write-first-then-UI for critical paths.
A10. No disconnect handler for Firebase RTDB
- Pattern: User goes offline mid-session; presence/lock data stays "online" forever.
- Risk: Locks held by dead clients block everyone.
- Fix: Use
onDisconnect().remove()or.set(null)for ephemeral state.
Category D: Multi-Actor
A11. Auth state change mid-operation
- Pattern: User session expires while async op is in flight.
- Risk: Operation succeeds with old token, or fails with confusing error.
- Fix: Re-check auth at write time; handle token expiry explicitly.
A12. Admin override during user edit
- Pattern: Admin can modify the same record a user is currently editing.
- Risk: User's save overwrites admin's change, or vice versa.
- Fix: Use a version field /
updatedAttimestamp + conflict detection on save.
A13. Same user, two devices
- Pattern: User edits on phone and laptop simultaneously.
- Risk: Last-device-wins; the other device's work is lost.
- Fix: Real-time sync (Firebase RTDB listeners) or "your data changed elsewhere, reload" prompt.
Audit output format
AUDIT of: <file or function>
Pattern matches:
- [A2] Line 47: client-side ID generation
Risk: Order ID duplication when 2+ branches submit simultaneously
Fix: Use firebase.database().ref('counters/orderId').transaction(...)
- [A8] Line 112: Firebase write without error handler
Risk: Silent data loss
Fix: Add .catch() with user-facing error toast
Pattern matches found: 2
Recommended priority: P0 (A2 is reproducible bug), P1 (A8 is data-loss risk)Tony's stack — known hotspots
These are the recurring places where these bugs show up in Tony's projects. When auditing or pre-flighting features in these files, weight these patterns higher:
PuffStick FC Order System (Firebase RTDB, single-file vanilla)
- Order ID generation → A2 (must use
transaction()) - Branch submits during admin edit → A12
- Branch goes offline mid-order, comes back → A4 / A10
- Telegram dispatcher in Cloudflare Worker → A1 (idempotency: same order, two dispatches?)
Pippa / Da'Neng Care (single-file, Firebase Auth + Firestore, localStorage cache)
- Multi-tab on same device → A4 (mother on phone + tablet both logging feed)
- Offline-first feed log → A4 / A10
- Sleep timer running across tab close → A7 (stale closure) / lifecycle
- MoonOwlAI Gemini call mid-auth-refresh → A11
PuffStick POS (Google Apps Script + Sheets)
- Concurrent receipt writes → A1 (must use
LockService.getScriptLock()) - POS ↔ FC Order schema sync → A3 (two systems writing the same source of truth)
PuffStick Production App
- Production batch counters → A2
- Multiple factory devices logging same batch → A3
How to invoke this skill
Pre-flight (default for new features):
"Run pre-flight for: adding VAT field to invoices" "I want to let branches edit their own pending orders — pre-flight first"
Audit (for existing code):
"Audit index.html for concurrency bugs" "Audit the order submission function" "Run the hidden-bugs checklist against this Apps Script"
Both (for an intermittent bug):
"Order IDs are duplicating sometimes — audit + propose fix with pre-flight"
Anti-patterns this skill prevents (what AI agents do without it)
- เดา (guessing): "It probably won't happen" — assume single-actor when there are many.
- ทำเกิน (over-engineering): Add locks everywhere instead of identifying real concurrent paths.
- ลืม (forgetting): Implement feature today, forget to check offline behavior, ship.
- โกหก (lying): Say "I've handled concurrency" without naming the specific strategy.
This skill exists specifically to combat ลืม + เดา for concurrency. It forces explicit naming of strategy (Q2, Q6) rather than vague "should be fine."
Reference: when atomic operations are required
You MUST use an atomic operation (not read-modify-write) for:
- Any counter (order ID, invoice number, batch number, queue position)
- Any "is this slot taken" check (table reservation, branch slot)
- Any balance / inventory decrement (stock, credit, points)
- Any "first one wins" semantic (claim, lock, assign)
Firebase RTDB: ref.transaction(fn) or ref.set(firebase.database.ServerValue.increment(1))
Firestore: runTransaction() or FieldValue.increment()
Apps Script: LockService.getScriptLock() with waitLock(timeout)
localStorage: No atomic op exists. Use Firebase as source of truth, localStorage as cache only.
Final rule
If the user describes a feature and you find yourself thinking "this probably won't have concurrency issues" — that is the signal to run pre-flight anyway. The bugs this skill catches are the bugs you don't see coming.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

