git clone https://github.com/cevr/gent.git
cd gent
bun installbun run typecheck # Must pass clean (no errors, no suggestions)
bun run test # Run product behavior tests- Telegraph style, minimal tokens
- Every service needs
Live+Testlayers - Schema validation everywhere
- Use
Effect.fnfor all service methods (required for tracing) - Use
Schema.TaggedClassfor discriminated unions - Use
export typefor interface re-exports
// Service definition
export class MyService extends Context.Tag("MyService")<
MyService,
MyServiceImpl
>() {
static Live: Layer.Layer<MyService> = Layer.succeed(MyService, { ... })
static Test = (): Layer.Layer<MyService> => Layer.succeed(MyService, { ... })
}
// Errors
export class MyError extends Schema.TaggedError<MyError>()("MyError", {
message: Schema.String,
}) {}
// Data classes
export class MyData extends Schema.Class<MyData>("MyData")({
id: Schema.String,
name: Schema.String,
}) {}See AGENTS.md for known gotchas:
bun:sqlite- Can't use vitest, usebun testSchema.ClassJSON roundtrip needsSchema.decodeUnknownSyncexactOptionalPropertyTypes- be explicit with interface types- Effect LSP suggestions (TS41) must be fixed
import { describe, test, expect } from "bun:test"
import { Effect } from "effect"
import { createTestLayer, createRecordingTestLayer } from "@gent/test-utils"
// Simple test
test("my test", async () => {
await Effect.runPromise(
Effect.gen(function* () {
// test code
}).pipe(Effect.provide(createTestLayer())),
)
})
// With sequence recording
test("records calls", async () => {
await Effect.runPromise(
Effect.gen(function* () {
const recorder = yield* SequenceRecorder
// do work
const calls = yield* recorder.getCalls()
assertSequence(calls, [{ service: "Provider", method: "stream" }])
}).pipe(Effect.provide(createRecordingTestLayer())),
)
})- Fork the repo
- Create a feature branch
- Make changes
- Run
bun run typecheck && bun run test - Submit PR
Read ARCHITECTURE.md before making significant changes. Update it when diverging from the documented design.