Deep implementation details of the IterablePlayer state machine, tick loop, and data source iteration patterns.
73%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./.github/skills/player-internals/SKILL.mdpreinit ──► initialize ──► start-play ──► idle
│ ▲
▼ │
play
│
▼
seek-backfill ──► idle
idle/play ──► reset-playback-iterator ──► idle/play (re-enters)
any ──► closepreinit → initialize: triggered the first time playback starts after construction (source supplied via constructor).initialize → start-play: Source initialize() resolved, topics/schemas availablestart-play → idle: Initial backfill complete, first state emittedidle → play: User presses play or setPlaybackSpeed(speed > 0)play → idle: Reached end of data or user pausesplay → seek-backfill: User seeks during playbackidle → seek-backfill: User seeks while pausedseek-backfill → idle: Backfill messages found, state emitted// Simplified tick loop logic
async #statePlay() {
const tickStart = performance.now();
const budgetMs = 300; // Max time per tick before yielding to UI
while (performance.now() - tickStart < budgetMs) {
const result = await this.#iterator.next();
if (result.done) { return "idle"; }
this.#pendingMessages.push(result.value.msgEvent);
// Check if we've passed the target wall-clock time
if (this.#hasReachedPlaybackTarget()) { break; }
}
this.#emitState();
return "play"; // continue playing next tick
}#emitStateImpl() is scheduled via queueMicrotask to coalesce rapid updatesactiveData (messages, currentTime, topics), progress (caching status)There is no concrete DataSource type in this layering. Sources implement one of two interfaces:
ISerializedIterableSource (yields raw bytes) or IDeserializedIterableSource (yields decoded
MessageEvents). A serialized source must be wrapped by DeserializingIterableSource; an
already-deserialized source skips that wrapper.
Concrete source (e.g. McapIndexedIterableSource, RemoteFileReadable-backed, WebSocket, …)
│ implements ISerializedIterableSource OR IDeserializedIterableSource
▼
DeserializingIterableSource (ONLY for serialized sources — applies parseChannel-based decode)
│ packages/suite-base/src/players/IterablePlayer/DeserializingIterableSource.ts
▼
CachingIterableSource (LRU block cache, ~600MB budget)
│
▼
BufferedIterableSource (producer-consumer, read-ahead, default { sec: 10 })
│
▼
IterablePlayer (tick loop consumes messages)⚠️
DeserializingIterableSourceis optional — it is only inserted when the underlying source is serialized (ISerializedIterableSource). Sources that already returnIDeserializedIterableSourcebypass it.
IIterableSourcealso exposes optionalprewarm?(): Promise<void>for pooled/multi-file sources to warm up before becoming active; see.github/skills/remote-caching/SKILL.mdforHydratedSourcePool.
When seeking to time T:
MessagePipeline.setSubscriptions()reset-playback-iterator state: when subscriptions change mid-play, iterator must restart from current timeawait in the loop allows microtask schedulingWorkerIterableSource off main threadlichtblick-suite/lichtblick
9589f38
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.