Repository navigation
Give Result Effect's shape, encode snapshots, and add an Effect playbook - #58
Closed
SandroMaglione wants to merge 3 commits into
Closed
SandroMaglione wants to merge 3 commits into
SandroMaglione wants to merge 3 commits into
Conversation
Ritmo's Result was { ok, value | error } while Effect's is
{ _tag, success | failure }, so every Effect game wrote the same
adapters, and Effect's Schema functions could not guard a descriptor
without wrapping each one. With the same plain fields, an Effect result
is a Ritmo result as it is, with no dependency on Effect. The tick,
Runtime.make, and Standard Schema failures now carry a _tag too, so
they narrow (and catchTag) like every other Ritmo error.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwwnHMKTTiVBpH9SivGeLk
A snapshot saved the live value and the host serialized it, so a component holding a Map, a Set, a class, or a value a schema library transforms (an Effect Option) saved a shape its decode could not read back. A constructor can now carry encode, the inverse of decode, and snapshot() saves what it returns; values without one are untouched. snapshot() returns a Result because an encoder can reject a value that went into the world through typed commands. Effect's Schema.encodeResult plugs in as an encoder as it is. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwwnHMKTTiVBpH9SivGeLk
The tick's MissingRuntimeRequirements and TickOrderViolation, FixedLoop's failures, and ritmo-math's errors still used kind or tag, so a host matching failures (or Effect's catchTag) saw two conventions. A constructed descriptor's decode is now a known key, checked against its value. Without it, writing Schema.decodeResult(schema) and Schema.decodeUnknownResult(schema) inline in the constructor left the value uninferred and failed to compile. The ritmo skill gets a playbook for using Effect in the host. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GwwnHMKTTiVBpH9SivGeLk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Sandro · project thread
Makes Ritmo pair cleanly with Effect while keeping no dependency on it. Stacked on #57 (gamepad): merge that first, then retarget this to
main.Result has Effect's shape
{ _tag: "Success", success } | { _tag: "Failure", failure }, built withResult.succeed/Result.fail. It is the same plain data as Effect 4'sResult, soSchema.decodeResult,decodeUnknownResult, andencodeResultplug into a descriptor's constructor as they are. Going the other way takes a two-linefromRitmohelper, because Effect's type carries a TypeId andpipe._tag, soEffect.catchTagworks:runtime.tick:SystemFailure,MissingRuntimeRequirements,TickOrderViolationFixedLoop:InvalidFixedLoopOptions,InvalidTickDelta,UpdateFailureRuntime.make:RuntimeConstructionErrorfromStandardSchema:StandardSchemaIssuesritmo-math's errors (Vector2/Invalid, …), which usedtagdecodeis now a known key, checked against its value. Without that, writingSchema.decodeResult(schema)andSchema.decodeUnknownResult(schema)inline left the value uninferred, and the code failed to compile.dtslint/Result.tst.tscovers this.Snapshots encode
encode(Descriptor.Encoder), the inverse ofdecode.runtime.snapshot()saves what it returns, so aSet, aMap, a class, or a transformed schema value (an EffectOption) survivesJSON.stringifyand restores. Values without one are saved as they are.Decode.struct,array, andnullablecarry theencodeof the codecs inside them.fromStandardSchema(schema, { encode })takes one.runtime.snapshot()returns aResult, failing withUnencodableComponentorUnencodableResource.ritmo-devtools: a replay recorder skips a point the world cannot be saved at, andReplay.extractfails withSnapshotFailed.oxlint-plugin-ritmo:handle-tick-resultreportsJSON.stringify(runtime.snapshot())andstructuredClone(runtime.snapshot()).Effect playbook
packages/core/skills/ritmo/playbooks/effect.md, routed fromSKILL.md. It covers:Schema.toCodecJsonfromRitmoandcatchTagManagedRuntime)FixedLoop.startand the input sources in aScopeIts example was type-checked and run against Effect 4.0.2: an
Optionand aReadonlySetround-trip through a snapshot.Migration
The two changesets list each breaking fix as a diff. In short:
Determinism.check's report and the trace events keep their ownokfields, because they are reports, not Results.Checks
pnpm run checkpasses: typecheck, lint, 628 type tests, and 606 tests.pnpm pack:checkpasses.pnpm bench:checkreports the same regressions on this machine for the base commit (Add Gamepad actions, and rebinding for keyboard and gamepad #57) as for this branch, so they come from the machine and not from this change.Spirit Call moves to this once a release is published.
Generated by Claude Code