# `Raxol.AgentClientProtocol.MethodTable`
[🔗](https://github.com/DROOdotFOO/raxol-agent/blob/raxol_agent_client_protocol-v0.1.0/packages/raxol_agent_client_protocol/lib/raxol/agent_client_protocol/method_table.ex#L1)

Single source of truth for the ACP method vocabulary: every wire method
string, its direction, its JSON-RPC kind, the callback atom used for both
the behaviour `@callback` and the `Router` dispatch clause, its
param/result schema modules, its capability gate, and its layer.

The method set follows the pinned ACP JSON Schema `schema-v1.19.0`
(`meta.json`), not the f1729 reference implementation — f1729 has
`session/fork`/`session/set_model` (not in the pinned oracle) and lacks
`session/delete`/`session/close`/`logout`/`$/cancel_request` (which the
oracle has). The table is literal data in this module: the oracle copy at
`priv/schema-oracle/v1/meta.json` is a dev/test file in the repository, not
shipped in the package and not read at compile time; the package's test
suite checks the table against it.

## What lives here vs. what doesn't

This module owns `rows/0`, `rows/1`, `rows_for_side/1`, `capability_for/1`,
the `:capability` column data, and the compile-time table invariants below
(incl. invariant 7 guarding the capability paths). The negotiated-caps
PREDICATE (`Capabilities.negotiated?/2`) and the `Connection`-side
pre-filter for `layer: :protocol`/`:session_control` rows are consumers of
this table, not implemented here.

## Compile-time invariants (raise `CompileError` on violation)

1. `{direction, wire}` is unique across all rows.
2. `kind: :notification` implies `result == nil`; `kind: :request` implies
   `result != nil`.
3. `callback` is unique per handling side (the set of callbacks visible to
   `rows_for_side(:agent)` has no duplicates, ignoring `nil`; ditto
   `:client`) — the anti-name-drift invariant.
4. `layer in [:protocol, :session_control]` if and only if
   `callback == nil`.
5. `ext == nil` rows must not start with `"_"`; `ext == :raxol` rows must
   start with `"_raxol/"`; a `"$/"` wire prefix is only allowed on
   `layer: :protocol` rows.

A sixth check (every `params`/`result` module referenced must exist and
export `from_json/1`) runs in the package's test suite rather than at
compile time, to avoid compile-order knots between this module and the
`Schema.*` modules it references.

# `capability`

```elixir
@type capability() :: nil | :never | {:agent | :client, [atom()]}
```

How a row is capability-gated:

  * `nil` — baseline method, always available (no capability gate).
  * `{:agent | :client, path}` — a field path into the *receiver's own*
    negotiated capability struct (`Schema.AgentTypes.AgentCapabilities`
    for `:agent`, `Schema.ClientTypes.ClientCapabilities` for `:client`).
    `Raxol.AgentClientProtocol.Capabilities.negotiated?/2` walks the path;
    a truthy leaf ⇒ negotiated, anything else (missing field, `nil`,
    `false`) ⇒ NOT negotiated (fail closed). The path uses **Elixir struct
    field atoms**, not the JSON wire keys, because the snapshot resolved
    against is the decoded struct — see invariant 7 and the ORACLE-DIVERGENCE
    note below.
  * `:never` — the oracle gates this method by a capability the ported
    Elixir capability structs cannot express, so it is unconditionally NOT
    negotiated (fail closed, `-32601`). No row currently uses it.

### ORACLE-DIVERGENCE (schema-oracle v1.19.0 vs. ported f1729 structs)

The pinned oracle's capability tree is richer than the Elixir structs
ported from the older f1729 snapshot; three oracle leaves that f1729 lacks
are ported so their methods gate on a real path:

  * `sessionCapabilities.{delete,close}` — ported as
    `Schema.AgentTypes.SessionDeleteCapabilities`/`SessionCloseCapabilities`
    onto the `SessionCapabilities` struct's `delete`/`close` fields.
    `session/delete`/`session/close` gate on
    `{:agent, [:session_capabilities, :delete | :close]}`.
  * `agentCapabilities.auth.logout` — ported as
    `Schema.AgentTypes.AgentAuthCapabilities`/`LogoutCapabilities` onto the
    `AgentCapabilities` struct's `auth` field. `logout` gates on
    `{:agent, [:auth, :logout]}`.
  * JSON-key vs struct-field: paths name Elixir struct fields
    (`[:session_capabilities, :list]`, `[:file_system, :read_text_file]`,
    …), not oracle JSON keys (`[:sessions, :list]`, `[:fs, :read_text_file]`),
    which resolve against neither the oracle JSON nor the struct;
    invariant 7 guards the first segment against drift.

# `direction`

```elixir
@type direction() :: :client_to_agent | :agent_to_client | :both
```

Which side sends this row's wire method.

# `ext`

```elixir
@type ext() :: nil | :raxol
```

`nil` for the core v1.19.0 surface; `:raxol` for registered `_raxol/*` vendor rows.

# `kind`

```elixir
@type kind() :: :request | :notification
```

JSON-RPC 2.0 shape: does this method get a reply?

# `layer`

```elixir
@type layer() :: :app | :protocol | :session_control
```

`:app` — has an app-level `@callback` and `Router` dispatch clause.
`:protocol` — protocol-level (`$/cancel_request`); `Connection` pre-filters
before `Router.decode/4` is ever called.
`:session_control` — app-shaped wire method, but Connection-routed with NO
app callback (currently only `session/cancel`).

# `row`

```elixir
@type row() :: %{
  wire: String.t(),
  direction: direction(),
  kind: kind(),
  callback: atom() | nil,
  params: module() | nil,
  result: module() | nil,
  capability: capability(),
  layer: layer(),
  ext: ext()
}
```

# `cap_fields`

```elixir
@spec cap_fields(:agent | :client) :: [atom()]
```

The literal top-level field atoms of a side's capability struct (drift-guarded by invariant 7 + tests).

# `capability_for`

```elixir
@spec capability_for(String.t()) ::
  :none | :never | :unknown_method | {:agent | :client, [atom()]}
```

The capability gate for a wire `method`, normalized for
`Raxol.AgentClientProtocol.Capabilities.negotiated?/2`:

  * `:none` — baseline method, always available (table `capability: nil`).
  * `:never` — unrepresentable in the ported caps model; fail closed.
  * `{:agent | :client, path}` — a field path into the receiver's own caps.
  * `:unknown_method` — `method` is not in the table (`decode/4` will
    `-32601` it; the caller MUST NOT treat this as a capability denial).

# `depend_on_source`
*macro* 

Register a compile-time dependency on this table's SOURCE FILE into the calling
module.

Any module that expands `rows/0` data into its own compiled output — the
`Router` decode/dispatch/result_marker clauses, the `Agent`/`Client` `@callback`
surface — MUST call this in its module body. A plain `rows/0` call is only an
EXPORTS dependency (recompile when `rows/0`'s API changes); a row-DATA edit does
not change that signature, so without this an incremental build leaves the
generated code stale (a new row present in the table but absent from
`Router`/`Agent` until a `mix clean`).
`@external_resource` forces the caller to recompile whenever this file changes.

# `rows`

```elixir
@spec rows() :: [row()]
```

All table rows, in declaration order (agent methods, then client methods, then protocol methods).

# `rows`

```elixir
@spec rows(direction()) :: [row()]
```

Rows whose `direction` matches exactly.

# `rows_for_side`

```elixir
@spec rows_for_side(:agent | :client) :: [row()]
```

Rows a given handling side must be able to decode/dispatch: `:agent` sees
everything sent `:client_to_agent` plus `:both`; `:client` sees everything
sent `:agent_to_client` plus `:both`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
