Claude-skill-registry Builtin Developer
Guides development of AILANG builtin functions. Use when user wants to add a builtin function, register new builtins, or understand the builtin system. Reduces development time from 7.5h to 2.5h (-67%).
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/builtin-developer" ~/.claude/skills/majiayu000-claude-skill-registry-builtin-developer && rm -rf "$T"
skills/data/builtin-developer/SKILL.mdBuiltin Developer
Add new builtin functions to AILANG using the modern registry system (M-DX1).
Quick Start
Development time: ~2.5 hours (down from 7.5h with legacy system)
Most common workflow:
- Register builtin with metadata (~30 min)
- Write hermetic tests (~1 hour)
- Validate and inspect (~30 min)
- Runtime auto-wires (already done!)
When to Use This Skill
Invoke this skill when:
- User wants to add a new builtin function
- User asks about builtin registration process
- User needs to understand the builtin system
- User wants to validate or inspect builtins
- User asks "how do I add a builtin?"
System Overview
Status: 🎉 M-DX1 COMPLETE (Oct 2025) - 52 builtins migrated, fully documented
Key Benefits:
- 67% faster: 2.5h instead of 7.5h
- 1 file instead of 4: Single-point registration
- 71% less code: Type Builder DSL reduces boilerplate
- Automatic wiring: Registry connects to runtime/link
- Hermetic tests: MockEffContext with HTTP/FS mocking
Components:
- Central Registry (
) - Single registration pointinternal/builtins/spec.go - Type Builder DSL (
) - Fluent type constructioninternal/types/builder.go - Test Harness (
) - Hermetic testing helpersinternal/effects/testctx/ - CLI Commands - Validation and inspection tools
Available Scripts
scripts/validate_builtins.sh
scripts/validate_builtins.shValidate all builtins in the registry.
Usage:
.claude/skills/builtin-developer/scripts/validate_builtins.sh
What it checks:
- All builtins are registered
- Proper metadata structure
- Type signatures valid
- Test coverage exists
scripts/check_builtin_health.sh
scripts/check_builtin_health.shRun ailang doctor and list commands.
Usage:
.claude/skills/builtin-developer/scripts/check_builtin_health.sh
Workflow
Step 1: Register the Builtin (~30 min)
Create or edit module file (e.g.,
internal/builtins/string.go):
// internal/builtins/string.go func init() { registerMyBuiltin() } func registerMyBuiltin() { RegisterEffectBuiltin(BuiltinSpec{ Module: "std/string", Name: "_str_reverse", NumArgs: 1, IsPure: true, // or false with Effect: "IO" Type: makeReverseType, Impl: strReverseImpl, Metadata: &BuiltinMetadata{ Description: "Reverse a string (Unicode-aware)", Params: []ParamDoc{ {Name: "s", Description: "String to reverse"}, }, Returns: "Reversed string", Examples: []Example{ {Code: `_str_reverse("hello")`, Description: "Returns \"olleh\""}, }, Since: "v0.3.15", Stability: StabilityStable, Tags: []string{"string", "reverse", "unicode"}, Category: "string", }, }) } func makeReverseType() types.Type { T := types.NewBuilder() return T.Func(T.String()).Returns(T.String()) } func strReverseImpl(ctx *effects.EffContext, args []eval.Value) (eval.Value, error) { str := args[0].(*eval.StringValue).Value runes := []rune(str) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } return &eval.StringValue{Value: string(runes)}, nil }
Key points:
- Use
inRegisterEffectBuiltin()init() - Use Type Builder DSL for type construction
- Include complete metadata (description, params, examples, tags)
- Set
for pure functions, orIsPure: true
for effectsEffect: "IO"
See: resources/type_builder_examples.md for type patterns
Step 2: Write Hermetic Tests (~1 hour)
Add tests in
internal/builtins/register_test.go:
func TestStrReverse(t *testing.T) { ctx := testctx.NewMockEffContext() tests := []struct { input string expected string }{ {"hello", "olleh"}, {"", ""}, {"🎉", "🎉"}, } for _, tt := range tests { result, err := strReverseImpl(ctx, []eval.Value{ testctx.MakeString(tt.input), }) assert.NoError(t, err) assert.Equal(t, tt.expected, testctx.GetString(result)) } }
Test Harness helpers:
- Create test contexttestctx.NewMockEffContext()
,testctx.MakeString()
,MakeInt()
- Create test valuesMakeRecord()
,testctx.GetString()
,GetInt()
- Extract valuesGetRecord()
For HTTP/FS effects:
ctx := testctx.NewMockEffContext() ctx.GrantAll("Net") // Grant Net capability ctx.SetHTTPClient(server.Client()) // Use test server
See: resources/testing_patterns.md for more examples
Step 3: Validate and Inspect (~30 min)
Run validation:
# Validate all builtins ailang doctor builtins # List all builtins by module ailang builtins list --by-module # Check for orphaned builtins (migration safety) ailang builtins check-migration
Run tests:
# Test specific builtin go test -v internal/builtins -run TestStrReverse # Test all builtins make test
Test in REPL:
ailang repl > :type _str_reverse string -> string > _str_reverse("hello") "olleh" : string
Step 4: Runtime Wiring (Automatic!)
No manual wiring needed! The registry automatically:
- ✅ Registers with evaluator (
)internal/eval - ✅ Adds to type environment (
)internal/types - ✅ Links to runtime (
)internal/runtime - ✅ Generates module interface (
)internal/link
Feature flag removed in v0.3.10 - New registry is now the default.
Key Components
Central Registry
Location:
internal/builtins/spec.go
Features:
- Single registration point with
RegisterEffectBuiltin() - Compile-time validation (arity, types, impl, effects)
- Freeze-safe (no registration after init)
- 52 builtins migrated
Type Builder DSL
Location:
internal/types/builder.go
Reduces type construction from 35 lines → 10 lines (-71%)
Available methods:
T := types.NewBuilder() // Primitive types T.String() T.Int() T.Float() T.Bool() T.Unit() // Complex types T.List(elementType) T.Record(fields...) T.Tuple(types...) T.Con("TypeName") T.Var("α") // Function types T.Func(arg1, arg2, ...).Returns(returnType).Effects("IO", "FS") // Type applications T.App("Result", okType, errType)
See: resources/type_builder_examples.md
Test Harness
Location:
internal/effects/testctx/
Value constructors:
→ StringValueMakeString(s string)
→ IntValueMakeInt(i int)
→ FloatValueMakeFloat(f float64)
→ BoolValueMakeBool(b bool)
→ ListValueMakeList([]Value)
→ RecordValueMakeRecord(map[string]Value)
Value extractors:
→ stringGetString(Value)
→ intGetInt(Value)
→ float64GetFloat(Value)
→ boolGetBool(Value)
→ []ValueGetList(Value)
→ map[string]ValueGetRecord(Value)
Effect mocking:
- Grant capabilitiesctx.GrantAll(effect)
- Mock HTTPctx.SetHTTPClient(client)
- Mock filesystemctx.SetFS(fs)
Validation & Inspection
Commands:
ailang doctor builtins # Health checks ailang builtins list # List all builtins ailang builtins list --by-module # Group by module ailang builtins list --by-effect # Group by effect ailang builtins check-migration # Check for orphaned builtins
6 validation rules:
- Type function exists and valid
- Implementation function exists
- Arity matches NumArgs
- Effect consistency (IsPure vs Effect field)
- Module name valid
- Metadata complete
Metrics
Development time:
| Task | Before | After | Savings |
|---|---|---|---|
| Files to edit | 4 | 1 | -75% |
| Type construction | 35 LOC | 10 LOC | -71% |
| Total time | 7.5h | 2.5h | -67% |
| Test setup | ~50 LOC | ~15 LOC | -70% |
Resources
Detailed Examples
For type patterns:
- resources/type_builder_examples.md - All type patterns
For testing patterns:
- resources/testing_patterns.md - Hermetic test examples
For comprehensive guide:
- M-DX1-FINAL-SUMMARY.md - Complete M-DX1 summary
- Design rationaledesign_docs/planned/easier-ailang-dev.md
(v0.3.10+) - Version historyCHANGELOG.md
File Locations
Builtin files by module:
- String functions (9 builtins)internal/builtins/string.go
- Math functions (37 builtins)internal/builtins/math.go
- IO functions (3 builtins)internal/builtins/io.go
- Network functions (1 builtin)internal/builtins/net.go
- Show typeclass (1 builtin)internal/builtins/show.go
- JSON decoding (1 builtin)internal/builtins/json_decode.go
Test files:
- Builtin testsinternal/builtins/*_test.go
- Test harness testsinternal/effects/testctx/*_test.go
Common Patterns
Pattern 0: "Zero-Arg" Builtins (M-DX10 Unit-Argument Model)
⚠️ AILANG has no true nullary functions! All functions that appear to take no arguments must take a
unit parameter.
// ✅ CORRECT - Unit-argument model RegisterEffectBuiltin(BuiltinSpec{ Module: "std/sharedmem", Name: "_sharedmem_keys", NumArgs: 1, // Takes unit parameter! Effect: "SharedMem", Type: func() types.Type { T := types.NewBuilder() return T.Func(T.Unit()).Returns(T.List(T.String())).Effects("SharedMem") }, Impl: func(ctx *effects.EffContext, args []eval.Value) (eval.Value, error) { // args[0] is unit, ignored (validates arity) keys := ctx.SharedMem.Cache.Keys() // ... }, }) // ❌ WRONG - True nullary (causes "arity mismatch: 0 vs 1") RegisterEffectBuiltin(BuiltinSpec{ Name: "_sharedmem_keys", NumArgs: 0, // BUG! })
Stdlib wrapper:
export func keys(u: unit) -> list[string] ! {SharedMem} { _sharedmem_keys(u) } -- Or expression body: export func now() -> int ! {Clock} = _clock_now()
Go tests must pass unit:
// ✅ Correct result, err := impl(ctx, []eval.Value{&eval.UnitValue{}}) // ❌ Wrong - causes arity mismatch result, err := impl(ctx, []eval.Value{})
Reference: M-DX10 design doc
Pattern 1: Pure String Function
RegisterEffectBuiltin(BuiltinSpec{ Module: "std/string", Name: "_str_len", NumArgs: 1, IsPure: true, Type: func() types.Type { T := types.NewBuilder() return T.Func(T.String()).Returns(T.Int()) }, Impl: func(ctx *effects.EffContext, args []eval.Value) (eval.Value, error) { s := args[0].(*eval.StringValue).Value return &eval.IntValue{Value: len([]rune(s))}, nil }, })
Pattern 2: Effect Function with HTTP
RegisterEffectBuiltin(BuiltinSpec{ Module: "std/net", Name: "_net_httpRequest", NumArgs: 4, Effect: "Net", Type: makeHTTPRequestType, Impl: effects.NetHTTPRequest, // Uses ctx.GetHTTPClient() })
Pattern 3: Complex Record Types
See: resources/type_builder_examples.md for full example with nested records.
Progressive Disclosure
This skill loads information progressively:
- Always loaded: This SKILL.md file (workflow overview)
- Execute as needed: Scripts in
(validation)scripts/ - Load on demand: Detailed type examples in
resources/
Notes
- No feature flag needed (default since v0.3.10)
- Registry auto-wires to all subsystems
- Test harness prevents network/FS side effects
- Metadata enables future tooling (docs, LSP)
- Type Builder DSL is type-safe at compile time