Files
gnoma/docs/essentials/domain-model.md
vikingowl efcb5a2901 docs: add project essentials (12/12 complete)
Vision, domain model, architecture, patterns, process flows,
UML diagrams, API contracts, tech stack, constraints, milestones
(M1-M11), decision log (6 ADRs), and risk register.

Key decisions: single binary, pull-based streaming, Mistral as M1
reference provider, discriminated unions, multi-provider collaboration
as core identity.
2026-04-02 18:09:07 +02:00

5.0 KiB

essential, status, last_updated, project, depends_on
essential status last_updated project depends_on
domain-model complete 2026-04-02 gnoma
vision

Domain Model

Entity Relationships

classDiagram
    class Session {
        +id: string
        +state: SessionState
        +Send(input) error
        +Events() chan Event
        +Cancel()
    }

    class Engine {
        +history: []Message
        +usage: Usage
        +Submit(input, callback) Turn
        +SetProvider(provider)
        +SetModel(model)
    }

    class Message {
        +Role: Role
        +Content: []Content
        +HasToolCalls() bool
        +ToolCalls() []ToolCall
        +TextContent() string
    }

    class Content {
        +Type: ContentType
        +Text: string
        +ToolCall: ToolCall
        +ToolResult: ToolResult
        +Thinking: Thinking
    }

    class Provider {
        <<interface>>
        +Stream(req) Stream
        +Name() string
    }

    class Stream {
        <<interface>>
        +Next() bool
        +Current() Event
        +Err() error
        +Close() error
    }

    class Tool {
        <<interface>>
        +Name() string
        +Execute(args) Result
        +IsReadOnly() bool
    }

    class Turn {
        +Messages: []Message
        +Usage: Usage
        +Rounds: int
    }

    class Elf {
        <<interface>>
        +ID() string
        +Send(msg) error
        +Events() chan Event
        +Wait() ElfResult
    }

    Session "1" --> "1" Engine : owns
    Engine "1" --> "1" Provider : uses
    Engine "1" --> "*" Tool : executes
    Engine "1" --> "*" Message : history
    Engine "1" --> "*" Turn : produces
    Message "1" --> "*" Content : contains
    Provider "1" --> "*" Stream : creates
    Stream "1" --> "*" Event : yields
    Session "1" --> "*" Elf : spawns (future)
    Elf "1" --> "1" Engine : owns

Glossary

Term Definition Example
gnoma The host application — single binary, agentic coding assistant gnoma "list files"
Elf A sub-agent (goroutine) with its own engine, history, and provider. Named after the elf owl. Background elf exploring auth/ on Ollama
Session A conversation boundary between UI and engine. Owns one engine, communicates via channels. TUI session, CLI pipe session
Engine The agentic loop orchestrator. Manages history, streams from provider, executes tools, loops until done. Engine running on Mistral with 5 tools
Provider An LLM backend adapter. Translates gnoma types to/from SDK-specific types. Anthropic provider, OpenAI-compat provider
Stream Pull-based iterator over streaming events from a provider. Unified interface across all SDKs. for s.Next() { e := s.Current() }
Event A single streaming delta — text chunk, tool call fragment, thinking trace, or usage update. EventTextDelta{Text: "hello"}
Message A single turn in conversation history. Contains one or more Content blocks. User text message, assistant message with tool calls
Content A discriminated union within a Message — text, tool call, tool result, or thinking block. Content{Type: ContentToolCall, ToolCall: &ToolCall{...}}
ToolCall The model's request to invoke a tool, with ID, name, and JSON arguments. {ID: "tc_1", Name: "bash", Args: {"command": "ls"}}
ToolResult The output of executing a tool, correlated to a ToolCall by ID. {ToolCallID: "tc_1", Content: "file1.go\nfile2.go"}
Turn The result of a complete agentic loop — may span multiple API calls and tool executions. Turn with 3 rounds: stream → tool → stream → tool → stream → done
Accumulator Assembles a complete Response from a sequence of streaming Events. Shared across all providers. Text fragments → complete assistant message
Callback Function the engine calls for each streaming event, enabling real-time UI updates. func(evt stream.Event) { ch <- evt }
Round A single API call within a Turn. A turn with 2 tool-use loops has 3 rounds. Round 1: initial query. Round 2: after tool results.
Routing Directing tasks to different providers based on capability, cost, or latency rules. Complex reasoning → Claude, quick lookups → local Qwen

Invariants

Rules that must always hold true in the domain:

  • A Message always has at least one Content block
  • A ToolResult always references a ToolCall.ID from the preceding assistant message
  • A Session owns exactly one Engine; an Engine is owned by exactly one Session
  • An Elf owns its own Engine — no shared mutable state between elfs
  • The Accumulator produces exactly one Response per stream consumption
  • Content.Type determines which payload field is set — exactly one is non-nil
  • Thinking.Signature must round-trip unchanged through message history (Anthropic requirement)
  • Tool execution only happens when StopReason == ToolUse
  • Stream.Close() must be called after consumption, regardless of error state
  • Provider.Stream() is the only network boundary — all tool execution is local

Changelog

  • 2026-04-02: Initial version