Genesis Temporal - CHASM Layer
Documentation complète de la couche CHASM.
🧩 Qu'est-ce que CHASM ?
CHASM (Custom Heterogeneous Agent State Machine) est une couche personnalisée pour la coordination de machines à états hétérogènes.
🏗️ Architecture
📦 Component Interface
// chasm/component.go
package chasm
type Component interface {
LifecycleState(ctx Context) LifecycleState
mustEmbedUnimplementedComponent()
}
type TerminableComponent interface {
Component
Terminate(ctx MutableContext, req TerminateComponentRequest) (TerminateComponentResponse, error)
}
type RootComponent interface {
TerminableComponent
}
🔄 Lifecycle States
// chasm/statemachine.go
type LifecycleState int
const (
LifecycleStateUnspecified LifecycleState = 0
LifecycleStateRunning LifecycleState = 2 << iota // OPEN
LifecycleStateCompleted // CLOSED
LifecycleStateFailed // CLOSED
)
func (s LifecycleState) IsClosed() bool {
return s == LifecycleStateCompleted || s == LifecycleStateFailed
}
func (s LifecycleState) String() string {
switch s {
case LifecycleStateRunning:
return "OPEN"
case LifecycleStateCompleted:
return "CLOSED_COMPLETED"
case LifecycleStateFailed:
return "CLOSED_FAILED"
default:
return "UNSPECIFIED"
}
}
🎯 Transitions
// Transition type
type Transition[S comparable, SM StateMachine[S], E any] struct {
Sources []S
Destination S
apply func(SM, MutableContext, E) error
}
// Exemple de transition
func NewWorkflowTransition() Transition[WorkflowState, Workflow, WorkflowEvent] {
return Transition[WorkflowState, Workflow, WorkflowEvent]{
Sources: []WorkflowState{WorkflowStateRunning},
Destination: WorkflowStateCompleted,
apply: func(wf Workflow, ctx MutableContext, event WorkflowEvent) error {
wf.SetState(WorkflowStateCompleted)
return nil
},
}
}
// Validation de transition
func (t Transition) Possible(sm StateMachine) bool {
currentState := sm.StateMachineState()
for _, source := range t.Sources {
if source == currentState {
return true
}
}
return false
}
📊 Field System
// chasm/field.go
type Field[T any] struct {
name string
value T
isSet bool
isMutable bool
validators []Validator[T]
onChange []func(T, T)
}
// Création de champ
func NewField[T any](name string, opts ...FieldOption[T]) *Field[T] {
field := &Field[T]{name: name}
for _, opt := range opts {
opt(field)
}
return field
}
// Options
func WithDefault[T any](value T) FieldOption[T] {
return func(f *Field[T]) {
f.value = value
f.isSet = true
}
}
func WithValidator[T any](validator Validator[T]) FieldOption[T] {
return func(f *Field[T]) {
f.validators = append(f.validators, validator)
}
}
// Get/Set
func (f *Field[T]) Get() (T, error) {
var zero T
if !f.isSet {
return zero, &FieldNotSetError{Field: f.name}
}
return f.value, nil
}
func (f *Field[T]) Set(value T) error {
for _, validator := range f.validators {
if err := validator(value); err != nil {
return &FieldValidationError{Field: f.name, Error: err}
}
}
oldVal := f.value
f.value = value
f.isSet = true
for _, callback := range f.onChange {
callback(oldVal, value)
}
return nil
}
🔧 Engine Implementation
// chasm/engine.go
type Engine struct {
registry *Registry
activeExecutions map[ExecutionID]*Execution
mu sync.RWMutex
config EngineConfig
}
type EngineConfig struct {
MaxConcurrentExecutions int
ExecutionTimeout time.Duration
RetryPolicy RetryPolicy
}
// Démarrer une exécution
func (e *Engine) StartExecution(
ctx context.Context,
componentID string,
input []byte,
) (ExecutionID, error) {
e.mu.Lock()
defer e.mu.Unlock()
if len(e.activeExecutions) >= e.config.MaxConcurrentExecutions {
return "", &MaxExecutionsReachedError{Max: e.config.MaxConcurrentExecutions}
}
componentType, err := e.registry.GetComponent(componentID)
if err != nil {
return "", err
}
component, err := componentType.CreateInstance(input)
if err != nil {
return "", err
}
execID := ExecutionID(generateUUID())
execution := &Execution{
ID: execID,
Component: component,
State: ComponentStateRunning,
StartTime: time.Now(),
}
e.activeExecutions[execID] = execution
go e.runExecution(ctx, execution)
return execID, nil
}
📋 Registry
// chasm/registry.go
type Registry struct {
components map[string]ComponentType
tasks map[string]TaskType
libraries map[string]Library
mu sync.RWMutex
}
// Enregistrer un composant
func (r *Registry) RegisterComponent(name string, componentType ComponentType) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, exists := r.components[name]; exists {
return &ComponentAlreadyRegisteredError{Name: name}
}
r.components[name] = componentType
return nil
}
// Obtenir un composant
func (r *Registry) GetComponent(name string) (ComponentType, error) {
r.mu.RLock()
defer r.mu.RUnlock()
componentType, exists := r.components[name]
if !exists {
return nil, &ComponentNotFoundError{Name: name}
}
return componentType, nil
}
// Lister les composants
func (r *Registry) ListComponents() []string {
r.mu.RLock()
defer r.mu.RUnlock()
names := make([]string, 0, len(r.components))
for name := range r.components {
names = append(names, name)
}
return names
}
🧪 Tests CHASM
// chasm/statemachine_test.go
func TestTransition_ValidSource(t *testing.T) {
transition := NewWorkflowTransition()
workflow := &Workflow{state: WorkflowStateRunning}
if !transition.Possible(workflow) {
t.Error("Expected transition to be possible")
}
}
func TestTransition_InvalidSource(t *testing.T) {
transition := NewWorkflowTransition()
workflow := &Workflow{state: WorkflowStateCompleted}
if transition.Possible(workflow) {
t.Error("Expected transition to be impossible")
}
}
func TestField_SetAndGet(t *testing.T) {
field := NewField[string]("test", WithDefault("default"))
value, err := field.Get()
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if value != "default" {
t.Errorf("Expected 'default', got '%s'", value)
}
}
Version : 1.0.0
Fichiers CHASM : 63 fichiers Go