Skip to content

[host-scope] mcode team hooks: event:audit + event:anomaly + permission:request #28

Description

@modacker

[host-scope, out of plugin] mcode team hooks for governance parity

⚠️ This issue is NOT for the plugin maintainer. It is a
coordination artifact for the mcode team. Plugin-side work
is in issues 1–4 of the same series. If @Wzdhehe is not also the
mcode maintainer, please forward this issue (or its content) to
the mcode team. The plugin-side PRs in issues 1–4 will reference
this issue's mcode-side PRs as a dependency before merging — we
will not start a "combined audit view" UI until mcode ships
event:audit + event:anomaly.


Author: @modacker
Source review: docs/REVIEW-sihankor-baselines-2026-08-28.md
Part 5.
Audience: mcode team (not the mcode-webui maintainer). This
issue is filed in the plugin repo as a coordination artifact; the
actual implementation lives in the mcode agent runtime.
Note: this issue is informational. The mcode-webui plugin
maintainer cannot implement these hooks; they exist to make the
host/plugin boundary explicit so the mcode team can plan.

Why

The mcode-webui plugin is bounded: it observes what mcode emits,
relays commands, and renders state. It does not (and cannot)
intercept mcode's internal writes to runtime-state.sqlite, its
internal audit trail, or its slash-command surface. Several
SiHankor engineering baselines require cooperation from the mcode
host to fully close:

  • Baseline 1 (LLM is symbol generator, not governor): the LLM
    in mcode can today trigger governance-flavored slash commands by
    parsing user input. A permission:request hook in mcode is the
    only way for the plugin to enforce that destructive operations
    require user confirmation.
  • Baseline 4 (audit trail): today, mcode's internal writes to
    its own sqlite are silent. The plugin's events.ndjson (see
    Stage 1 issue) is plugin-scope only. A mcode-side
    event:audit + event:anomaly SSE channel would let the plugin
    forward into a single combined audit view.
  • Prohibition 1 (LLM must not directly modify knowledge/intent):
    the plugin can guard webui-internal writes, but mcode's own
    state changes (session metadata, plan state, etc.) happen
    without any webui gating. A requires_confirmation: true flag
    on mcode's slash commands would let the plugin enforce this in
    the UI without rewriting the host.

What (in mcode, not in the plugin)

1. event:audit SSE channel

mcode emits structured events for every internal state change:

{ "kind": "session.create" | "session.update" | "plan.commit"
        | "tool.exec" | "tool.result" | "permission.ask"
        | "permission.resolve",
  "ts": ..., "actor": "user"|"mcode"|"system", "data": { ... } }

The plugin forwards these into its own events.ndjson (see
Stage 1 issue), prefixed with a clear src: "mcode" marker.

2. event:anomaly SSE channel

mcode emits anomaly events that the plugin surfaces through its
anomaly bell (see Stage 1 issue). Examples:

  • Repeated tool failures from the same call site
  • Permission escalation attempts
  • Plan / execution divergence (mcode committed a step that the
    approved plan didn't include)
  • Token-bucket near-exhaustion

The plugin renders these as a top-bar toast; the user sees
"⚠️ mcode reported 3 consecutive tool failures at "
without the plugin needing to know what a "tool failure" is.

3. permission:request hook on slash commands

mcode's slash command surface (today: a flat list parsed by mcode
itself) gains a requires_confirmation: boolean flag. The plugin
wires the flag to its authorize() helper (see Stage 1 issue):
when mcode is about to execute a requires_confirmation: true
command, the plugin intercepts, shows a confirmation modal, and
only lets mcode proceed when the user clicks Allow.

This is the cleanest way to enforce SiHankor prohibition 1
("LLM must not directly modify knowledge/intent") without
rewriting the host's command dispatch.

4. goal:status and plan:status events

mcode emits structured events when the goal/plan subsystem
transitions. The plugin renders these as the chat-side banner
status (e.g., "📋 Plan mode — 3/5 steps approved") without
needing to poll mcode's internal state.

Acceptance criteria

(Marked per-implementer; the plugin cannot test these directly.
The plugin team can test by stubbing the mcode-side events in
acp-client.js.)

  • mcode emits event:audit for every sessions.write,
    plan.commit, tool.exec, tool.result
  • mcode emits event:anomaly for the 4 categories above
  • mcode's slash command surface includes a
    requires_confirmation: boolean field on each command
  • mcode emits goal:status and plan:status events on
    transition
  • The plugin's combined audit view (plugin events + mcode
    events) renders in the top-bar "view" affordance without
    the user needing to read raw logs

Out of scope

  • Plugin-side implementation of any of these — that's the Stage
    1/2/3 issues
  • mcode's internal sqlite schema changes — out of plugin scope
  • Telemetry / observability beyond the audit trail — explicitly
    rejected in SECURITY-NOTES.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions