Matrix logo

Intent IR

The Intent IR is the central type in Matrix. Every interaction produces one. It carries the typed source-of-truth (Frame) alongside structured gaps (Unknowns) and grounding references.

The Intent IR is the central type in Matrix. Every interaction produces one. It carries the typed source-of-truth (Frame) alongside structured gaps (Unknowns) and grounding references. Everything downstream operates on this type, never on raw prose.

Source files: ir/intent.go, ir/plan.go, ir/plan_validate.go, ir/encode.go.


Intent

type Intent struct {
    ID      string `json:"id"`               // ULID
    Version string `json:"v"`                // "mcl/0.1"
    Parent  string `json:"parent,omitempty"` // parent IntentRef for sub-intents
    Actor   string `json:"actor"`            // who wants this (UserRef or AgentRef)
    Agent   string `json:"agent"`            // who will execute (AgentRef)

    Prose string `json:"prose"` // original NL goal (display only)

    Frame     Frame     `json:"frame"`                 // typed source of truth
    Unknowns  []Unknown `json:"unknowns,omitempty"`    // structured gaps
    References []Reference `json:"references,omitempty"` // grounding matrix:// URIs

    State      string  `json:"state"`      // IntentState
    Confidence float64 `json:"confidence"` // 0..1
    Budget     *Budget `json:"budget,omitempty"`
    Deadline   string  `json:"deadline,omitempty"` // ISO8601
    CreatedAt  string  `json:"created_at"`
    ExpiresAt  string  `json:"expires_at,omitempty"`
    GoalID     string  `json:"goal_id,omitempty"` // links to parent Goal

    SignedBy string          `json:"signed_by"` // actor's public key
    Hash     string          `json:"hash"`      // sha256 self-hash
    CompileMetadata *CompileMetadata `json:"compile_metadata,omitempty"` // D11 trace
}

Intent states

The Intent lifecycle follows a state machine:

StateDescription
draftInitial state from intent.draft
proposedAfter compilation (intent.compiled)
clarifyingWhile intent.clarify / intent.answer loop is active
acceptedAfter user signs off (intent.accept)
executingWhile the executor walks the plan
completedAfter intent.attest with outcome=success
failedAfter intent.fail
cancelledAfter intent.cancel

Frame

The Frame is the typed source of truth: what gets signed and executed.

type Frame struct {
    Verb            string       `json:"verb"`                       // D7 closed vocab
    Objects         []SlotEntry  `json:"objects"`                    // typed referents
    Constraints     []Constraint `json:"constraints,omitempty"`      // hard/soft predicates
    SuccessCriteria []Predicate  `json:"success_criteria,omitempty"` // completion checks
    Preferences     []Preference `json:"preferences,omitempty"`      // soft tie-breakers
}

SlotEntry

type SlotEntry struct {
    Name  string `json:"name"`           // slot name
    Value string `json:"value"`          // resolved value or NL text
    URI   string `json:"uri,omitempty"`  // resolved matrix:// URI (post D13)
    Type  string `json:"type,omitempty"` // type annotation
}

Constraint

Typed predicates that must hold throughout execution:

TypeFieldsDescription
budgetmax (AssetAmount)Spending cap
deadlineby (ISO8601)Completion deadline
jurisdictionallow, deny (string[])Geographic/legal scope
qualitymetric, min (float64)Quality threshold
rulerule (RuleRef)Rule matrix:// URI
policypolicy (Argus)Policy reference
x:*schema, dataCustom constraint

Hard constraints (hard: true) fail the intent if violated.

Predicate

Checkable completion criteria:

TypeFieldsDescription
deliveredartifactArtifact was delivered
signed_offby (UserRef)User approved
externalurl, checkExternal verification
attestationsource, topicAgent attestation
x:*schema, dataCustom predicate

Preference

Soft tie-breakers that do not fail the intent if violated:

type Preference struct {
    Rank   string   `json:"rank"`             // preference dimension
    Prefer []string `json:"prefer,omitempty"` // ordered preferences
}

Unknown

A typed gap blocking or delaying execution:

type Unknown struct {
    ID         string   `json:"id"`                    // local id ("u1", "u2")
    Field      string   `json:"field"`                 // SlotPath
    Type       string   `json:"type"`                  // expected type
    Severity   string   `json:"severity"`              // "blocking" | "preferred" | "optional"
    Rationale  string   `json:"rationale"`             // human-readable why
    Default    string   `json:"default,omitempty"`     // suggested fill
    Options    []string `json:"options,omitempty"`     // enum-like choices
    SourceHint string   `json:"source_hint,omitempty"` // cortex location
}

Blocking unknowns prevent intent.accept until resolved. Preferred unknowns produce clarify questions. Optional unknowns are surfaced in the UI without interrupting flow.


PlanTree

The PlanTree is the typed plan structure produced by skills running under the executor model:

type PlanTree struct {
    ID          string     `json:"id"`                    // ULID
    Version     string     `json:"v"`                     // "mcl/0.1"
    IntentID    string     `json:"intent_id"`             // back-reference
    CreatedAt   string     `json:"created_at"`            // ISO-8601
    CreatedBy   string     `json:"created_by"`            // matrix://agent/<did>
    SkillRef    string     `json:"skill_ref"`             // version-pinned skill URI
    ModelDigest string     `json:"model_digest,omitempty"`
    Root        PlanNode   `json:"root"`                  // entry point
    Budget      *Budget    `json:"budget,omitempty"`
    Hash        string     `json:"hash"`                  // sha256 self-hash
}

PlanNode

A discriminated union by Kind:

KindPayloadChildrenDescription
sequential(none)>= 1Runs children in order
parallel(none)>= 1Runs children concurrently
stepStep0In-skill LLM prompt
tool_callToolCall0Single tool invocation
sub_dispatchSubDispatch0Sub-skill or sub-agent dispatch
gateGate0Human-in-loop policy gate

StepPayload

type StepPayload struct {
    PromptName      string            `json:"prompt_name,omitempty"`
    Inputs          map[string]string `json:"inputs,omitempty"`
    ExpectedOutputs []string          `json:"expected_outputs,omitempty"`
    Kind            string            `json:"kind,omitempty"` // "reason", "code", "summarize", etc.
}

The Kind field drives executor-tier model routing. Closed set: reason, code, summarize, write, transform, classify, hard_reason.

ToolCallPayload

type ToolCallPayload struct {
    ToolRef         string            `json:"tool_ref"`                   // version-pinned matrix:// URI
    Args            map[string]string `json:"args,omitempty"`
    TimeoutMs       int               `json:"timeout_ms,omitempty"`
    SideEffectClass string            `json:"side_effect_class,omitempty"` // "read", "write", "network", "shell", "chain"
}

SubDispatchPayload

type SubDispatchPayload struct {
    SkillRef  string `json:"skill_ref"`            // version-pinned sub-skill URI
    AgentRef  string `json:"agent_ref,omitempty"`  // target agent
    SubIntent *Frame `json:"sub_intent,omitempty"` // Frame for sub-intent
    ScopeURI  string `json:"scope_uri,omitempty"`  // CortexScope grant
}

Canonical encoding (D11)

The CanonicalJSON function encodes an Intent to canonical JSON with deterministic key ordering at every nesting level. Zero/empty values are omitted. The sha256 hash of this encoding is the Intent's Hash field:

func Hash(intent *Intent) (string, error) {
    // Clear self-referential hash field
    intent.Hash = ""
    canonical, _ := CanonicalJSON(intent)
    sum := sha256.Sum256(canonical)
    return fmt.Sprintf("%x", sum), nil
}

The same canonical encoding is used for PlanTree hashing via HashPlan.


D7 closed verbs

var D7ClosedVerbs = map[string]bool{
    "find": true, "acquire": true, "build": true, "modify": true,
    "deliver": true, "analyze": true, "negotiate": true, "schedule": true,
    "monitor": true, "delegate": true,
}

The x: namespace is available for custom verbs: any verb starting with x: passes validation.


Plan validation

ValidatePlan enforces 8 structural invariants:

  1. PlanTree.ID, IntentID, SkillRef must be populated
  2. Every PlanNode has a unique non-empty ID within the tree
  3. Every PlanNode.Kind is in ValidNodeKinds
  4. Branch kinds (sequential, parallel) have >= 1 child and no terminal payload
  5. Terminal kinds (step, tool_call, sub_dispatch, gate) have no children and exactly the matching typed payload
  6. ToolCall.ToolRef and SubDispatch.SkillRef are version-pinned (must contain @)
  7. ToolCall.SideEffectClass (if set) is in ValidSideEffectClasses
  8. Gate.Question is non-empty