
yield-injections
Use when working with Turso yield injection or synthetic failure injection machinery for safe resumable state-machine boundaries, including YieldInjector, FailureInjector, YieldPointMarker, injecttransitionyield!, injectioyield!, injecttransitionfailure!, CommitYieldPoint, CheckpointYieldPoint, CursorYieldPoint, deterministic interleaving tests, abandoned statement/commit tests, and concurrent-simulator yield plans.
- Installs
- 41
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/tursodatabase/turso --skill yield-injections- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads yield-injections automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Yield Injection Guide
Yield injection is test-only infrastructure for forcing cooperative-yield or failure boundaries in resumable state machines. The implementation currently lives under core/mvcc/, but the mechanism is not MVCC semantics.
Key Files
core/mvcc/yield_points.rs: injector traits and macros.core/mvcc/yield_hooks.rs:YieldPointMarker,YieldContext,ProvidesYieldContext.core/connection.rs: per-connection injector slots andyield_instance_id_counter.core/mvcc/database/mod.rs: commit points and commit cleanup.core/mvcc/database/checkpoint_state_machine.rs: checkpoint points.core/mvcc/cursor.rs: cursor points.core/mvcc/database/tests.rs: fixed test injectors and regression examples.testing/concurrent-simulator/yield_injection.rs: simulator injector.
All hooks are behind cfg(any(test, injected_yields)); injected_yields means test_helper or simulator.
Core Model
YieldPoint { ordinal, point_count } identifies one hook in one point family. Families are #[repr(u8)] enums implementing YieldPointMarker; ordinal is source-order self as u8, and point_count comes from EnumCount.
Do not reorder existing yield-point variants. Simulator plans store raw ordinals, not names.
Why reordering matters: ordinal is self as u8, so source order is part of the deterministic schedule. If a seed planned "yield at ordinal 2", reordering can silently retarget that same seed from one conceptual hook to another. This makes CI seeds, bisects, and simulator coverage hard to reproduce. Appending a variant still changes point_count and can perturb future plans, but it preserves the meaning of existing ordinals.
Each yield-capable live object has:
instance_id: distinguishes simultaneous state machines/cursors.selection_key: stable logical-operation key used by deterministic plans.- active connection injectors:
yield_injector()andfailure_injector().
Hook Macros
inject_transition_yield!(self, Point): forStateTransitionreturningTransitionResult<T>.inject_io_yield!(self, Point): for cursor/helper functions returningIOResult<T>.inject_transition_failure!(self, Point): returnsErr(LimboError)fromStateTransition.
There is no inject_io_failure! today; failure injection only works for TransitionResult state machines.
Injected yields return Completion::new_yield(). It is already finished, but is_explicit_yield() is true, so the VDBE surfaces it as StepResult::Yield instead of continuing immediately.
New Yield-Capable Type
For a new state machine/cursor family, add all of this behind cfg(any(test, injected_yields)):
yield_instance_id: u64,
Initialize it from the connection:
yield_instance_id: connection.next_yield_instance_id(),
Implement ProvidesYieldContext:
impl ProvidesYieldContext for MyStateMachine {
fn yield_context(&self) -> YieldContext {
YieldContext::new(
self.connection.yield_injector(),
self.connection.failure_injector(),
self.yield_instance_id,
my_yield_key(self.logical_operation_id),
)
}
}
Add a family-specific *_yield_key(...) -> u64 helper. Mix stable logical identity, such as tx id/table id, with a family tag so simulator plans do not collide across families. Do not use wall-clock time, random values, allocation addresses, or incidental counters unrelated to the logical operation.
Adding A Point
Before adding a hook, prove re-entry is safe. A synthetic yield returns StepResult::Yield; the same statement/state machine may be stepped again immediately later. On re-entry, it must resume from an explicit state, not repeat non-idempotent work.
Rules:
- Mutate the state machine into the resumable state before yielding.
- Do not put a hook before a
push,insert, counter increment, lock acquisition, or cleanup action unless repeating that action is harmless or explicitly guarded by state. - If a lock/guard is held across the yield, test both resume and drop-at-yield paths.
- For abandonment tests, dropping the statement at the yield must restore invariants through Drop/abort cleanup.
- When adding a new yield point, always add it at the last position in the enum to preserve existing ordinals.
- Do not reorder variants in existing
YieldPointMarkerenums, unless absolutely required. Reordering changes the meaning of existing ordinals and can break reproducibility of CI seeds and bisects.
Checklist:
- Append a variant to the appropriate
*YieldPointenum. Do not reorder. - Place the macro after the transition that makes resume safe, or before a lock acquisition when explicitly testing lock interleavings.
- Avoid calling macros while borrowing
&mut self.state; they needself.yield_context(). - Add a deterministic test with
FixedYieldInjectororFixedFailureInjector. - If testing abandoned work, drop the statement at the yield and assert cleanup invariants.
For deeper re-entry/state-machine rules, also use async-io-model.
Fixed Unit-Test Injectors
Use fixed injectors in targeted tests:
use crate::mvcc::yield_hooks::YieldPointMarker;
conn.set_yield_injector(Some(FixedYieldInjector::new([
CommitYieldPoint::LogRecordPrepared.point(),
])));
FixedYieldInjector stores a HashSet<YieldPoint>, ignores instance_id/selection_key, and consumes each configured point once total. If two simultaneous instances hit the same point, the first one consumes it.
FixedFailureInjector behaves similarly but maps one point to one LimboError.
Clear injectors when reusing the same connection:
conn.set_yield_injector(None);
conn.set_failure_injector(None);
Common Test Targets
- Commit
LogRecordPrepared: leave commit inPreparing, interleave another writer/checkpoint, or drop the statement. - Commit
BeforeCommittedTimestampWatermarkUpdate: test out-of-order completion and monotonic watermarks. - Commit
BeforeFinishCommittedTx: test abandonment after committed state but before final cleanup. - Commit
AfterRemoveTxfailure: verify tx maps, connection tx slots, locks, and exclusive tx atomics are not stranded. - Checkpoint
BeforeAcquireLock: interleave before checkpoint boundary sampling. - Checkpoint
AfterDurableBoundaryAdvancedfailure: test retry/recovery after durable state advanced. - Cursor
NextStartorSeekStart: test cursor re-entry and dropped-statement cleanup, especially rowid allocator locks.
Abandoned commit tests rely on cleanup paths: CommitStateMachine::drop calls cleanup_unfinished_commit, and abort-side cleanup runs through cleanup_abandoned_mvcc_commit.
Simulator Use
The concurrent simulator owns randomized deterministic injection. Do not install FixedYieldInjector from simulator code.
Runtime:
- Each fiber owns an
Arc<SimulatorYieldInjector>for the current operation. - Operation init replaces it with
SimulatorYieldInjector::new(fiber_yield_seed(seed, fiber_idx)). - Every
stmt.step()goes throughstep_stmt_with_injected_yield, which installs the injector, steps once, then clears it via RAII.
Planning:
- Plans are keyed by
(instance_id, selection_key, point_count). MAX_YIELDS = 20.- For each key, the simulator chooses a random count in
0..=MAX_YIELDS; zero injected yields is valid. - It fills that many slots with random ordinals in
0..point_count. - Matching slots are consumed once; duplicate ordinals let the same hook fire multiple times.
Simulator reproducibility:
- Same top-level seed and fiber count are required to reproduce a schedule.
- Reordering variants changes ordinal meaning.
- Adding variants changes
point_count, which can perturb future schedules for the same seed. - Changing hook placement or fiber assignment can make the same seed explore a different schedule.
Gotchas
- Do not add test-only ad hoc
Completion::new_yield()if a yield point can express the interleaving. Runtime lock-contention yields are fine when semantics require them. - Do not place a hook before state is updated to the resumable state.
set_yield_injector(Some(...))asserts the slot is empty;set_yield_injector(None)asserts it is installed.- Do not rely on exact simulator yield counts outside
SimulatorYieldInjectortests.
FAQ
What does the yield-injections skill do?
Use when working with Turso yield injection or synthetic failure injection machinery for safe resumable state-machine boundaries, including YieldInjector, FailureInjector, YieldPointMarker, injecttransitionyield!, injectioyield!, injecttransitionfailure!, CommitYieldPoint, CheckpointYieldPoint, CursorYieldPoint, deterministic interleaving tests, abandoned statement/commit tests, and concurrent-simulator yield plans.
How do I install yield-injections?
Run npx skills add https://github.com/tursodatabase/turso --skill yield-injections in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/yield-injections/ (personal) or .claude/skills/yield-injections/ (project).
Which AI agents can use yield-injections?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is yield-injections safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains yield-injections?
It is published by tursodatabase in tursodatabase/turso. It has 41 recorded installs.
More from tursodatabase/turso
code-quality
tursodatabase/turso
General Correctness rules, Rust patterns, comments, avoiding over-engineering. When writing code always take these into account
index-knowledge
tursodatabase/turso
Generate hierarchical AGENTS.md knowledge base for a codebase. Creates root + complexity-scored subdirectory documentation.
debugging
tursodatabase/turso
How to debug tursodb using Bytecode comparison, logging, ThreadSanitizer, deterministic simulation, and corruption analysis tools
async-io-model
tursodatabase/turso
Explanations of common asynchronous patterns used in tursodb. Involves IOResult, state machines, re-entrancy pitfalls, CompletionGroup. Always use these patterns in core when doing anything IO
testing
tursodatabase/turso
How to write tests, when to use each type of test, and how to run them. Contains information about conversion of .test to .sqltest, and how to write .sqltest and rust tests
storage-format
tursodatabase/turso
SQLite file format, B-trees, pages, cells, overflow, freelist that is used in tursodb
Popular testing & qa skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
grilling
mattpocock/skills
Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
migrate-to-shoehorn
mattpocock/skills
Migrate test files from as type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace as in tests, or needs partial test data.
run-train
lllllllama/rigorpilot-skills
Rigor Train skill for deep learning research repositories. Use when a documented or selected training command should be run conservatively for startup verification, short-run verification, full kickoff, or resume, with command, config, seed, log, checkpoint, status, and metric evidence written to standardized trainoutputs/. Do not use for environment setup, exploratory sweeps, speculative idea implementation, or end-to-end orchestration.
test-driven-development
obra/superpowers
Use when implementing any feature or bugfix, before writing implementation code
finishing-a-development-branch
obra/superpowers
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work