6. Runtime View

Startup Sequence

sequenceDiagram
  participant FS as Filesystem
  participant OC as opencode
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine
  participant Config as ConfigLoader

  OC->>Plugin: load plugin (opencode.jsonc)
  Plugin->>Config: load(opencode-semantic-anchors.yaml)
  Config->>FS: read YAML file
  FS-->>Config: raw YAML
  Config->>Config: validate against Zod schema
  alt Config valid
    Config-->>Plugin: LoadedConfig { contracts, presets, settings }
    Plugin->>Engine: new RuleEngine(config)
    Engine-->>Plugin: ready
    Plugin-->>OC: Plugin loaded
  else Config invalid
    Config-->>Plugin: ValidationError
    Plugin-->>OC: Plugin loaded with defaults + warning
  end

  OC->>Plugin: agent.activate({ role: "software-developer" })
  Plugin->>Engine: setRole("software-developer")
  Engine->>Config: getActiveContracts("software-developer")
  Config-->>Engine: [contract-A, contract-B, ...]
  Engine-->>Plugin: SessionState { role, toolCallCount: 0 }
  Plugin-->>OC: ready

Tool Execution: Blocked Path

Occurs when an active contract triggers a BLOCK (e.g., Step Confirmation after 3 tool calls without "Weiter?"):

sequenceDiagram
  participant User
  participant OC as opencode
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine

  Note over User,Engine: Tool-Call #3 ohne vorheriges "Weiter?"
  User->>OC: "edit file src/main.ts"
  OC->>Plugin: tool.execute.before(edit, { file: "src/main.ts" })
  Plugin->>Engine: evaluate({ type: "tool", toolName: "edit" })
  Engine->>Engine: toolCallCount = 3, interval = 3, no confirmation
  Engine-->>Plugin: Verdict { allow: false, contract: { id: "step-confirmation" } }
  Plugin->>Plugin: throw new Error("🚫 Step Confirmation: ...")
  Plugin--xOC: Error: block message
  OC->>User: Show block message
  User->>OC: "/anchor bypass 'continuing intentional edit'"
  OC->>Plugin: anchor-bypass.execute({ reason: "continuing intentional edit" })
  Plugin->>Engine: incrementOverride()
  Engine-->>Plugin: overrideCount: 1/3
  Plugin-->>OC: "Override 1/3: ..."
  OC->>Plugin: tool.execute.before(edit, ...) [retry by LLM]
  Plugin->>Engine: evaluate({ type: "tool", toolName: "edit" })
  Engine-->>Plugin: Verdict { allow: true }
  Plugin-->>OC: proceed
  OC->>User: Execute edit

Tool Execution: Warn Path

Occurs when a contract matches in WARN mode (e.g., Source Anchor on write without prior source citation):

sequenceDiagram
  participant User
  participant OC as opencode
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine

  User->>OC: "write the implementation"
  OC->>Plugin: tool.execute.before(write, { file: "..." })
  Plugin->>Engine: evaluate({ type: "tool", toolName: "write" })
  Engine->>Engine: check message history for source citation
  Engine-->>Plugin: Verdict { allow: true, contract: { id: "source-anchor-warn" } }
  Plugin->>Plugin: client.app.log({ level: "warn", message: "⚠️ Source Anchor: ..." })
  Plugin-->>OC: proceed (no throw)
  OC->>User: Execute write + [warning in log]

Chat Message Flow

No blocking, only gentle reminders:

sequenceDiagram
  participant User
  participant OC as opencode
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine

  User->>OC: "refactor this module"
  OC->>Plugin: event({ type: "message.updated", content: "refactor this module" })
  Plugin->>Engine: evaluate({ type: "message", content: "refactor this module" })
  Engine->>Engine: check Intent (missing: what? why? verify?)
  Engine-->>Plugin: Verdict { allow: true, message: "No explicit intent detected" }
  Plugin->>Plugin: client.app.log({ level: "info", message: "Tip: ..." })
  Plugin-->>OC: proceed
  OC->>User: Process message (suggestion in log)

Agent Role Change

sequenceDiagram
  participant OC as opencode
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine
  participant Config as ConfigLoader

  OC->>Plugin: agent.activate({ role: "software-architect" })
  Plugin->>Engine: setRole("software-architect")
  Engine->>Config: getActiveContracts("software-architect")
  Config-->>Engine: [boundary-anchor, emergence-anchor, step-confirmation]
  Engine->>Engine: Reset toolCallCount
  Engine-->>Plugin: SessionState { role: "software-architect" }
  Plugin-->>OC: ready
  OC->>Plugin: agent.activate({ role: "software-developer" })
  Plugin->>Engine: setRole("software-developer")
  Engine->>Config: getActiveContracts("software-developer")
  Config-->>Engine: [step-confirmation, source-anchor, intent-anchor]
  Engine->>Engine: Reset toolCallCount
  Engine-->>Plugin: SessionState { role: "software-developer" }
  Plugin-->>OC: ready

Error Scenarios

Config Validation Error

sequenceDiagram
  participant FS as Filesystem
  participant Plugin as opencode-semantic-anchors
  participant Config as ConfigLoader

  Plugin->>Config: load("opencode-semantic-anchors.yaml")
  Config->>FS: read
  FS-->>Config: invalid YAML
  Config->>Config: log Warning
  Config-->>Plugin: DefaultConfig (built-in fallback)
  Plugin->>Plugin: continue with defaults

Hook Throw-Error

sequenceDiagram
  participant OC as opencode
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine

  OC->>Plugin: tool.execute.before(edit, ...)
  Plugin->>Engine: evaluate(...)
  Engine--xPlugin: throws Error
  Plugin->>Plugin: catch Error, log, do NOT re-throw
  Plugin-->>OC: proceed (tool executes)

Fail-Open principle: On any error in a hook, the plugin returns { allow: true } so the agent is not blocked. The error is logged. This behaviour is specified in 05-building-block-view.md section "Error Handling" (four scenarios: Config/RuleEngine/Hook/Tool).

Bypass Maximum Reached

sequenceDiagram
  participant User
  participant Plugin as opencode-semantic-anchors
  participant Engine as RuleEngine

  User->>Plugin: "/anchor bypass 'one more time'"
  Plugin->>Engine: getState()
  Engine-->>Plugin: SessionState { overrideCount: 3, maxOverrides: 3 }
  Plugin-->>User: "Error: Max overrides (3) reached. Cannot bypass."

Session Reset (Plugin Reload)

On plugin reload (e.g., after config change + /anchor config-reload):

  1. ConfigLoader re-reads YAML

  2. Validates against Zod schema

  3. RuleEngine is initialised with new config

  4. SessionState is reset (toolCallCount = 0, overrideCount = 0)

  5. Role is preserved (if set)