# core.adapter_manifest


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Design notes

**Matching semantics (v1, evidence-honest):** a protocol method matches
when the surface has a method of the same name AND — when both sides
record parameter names — the surface method’s params START WITH the
protocol’s (prefix rule: extra trailing, defaulted, surface params are
fine; missing or reordered ones are not). Annotations are recorded in
both manifests for diagnostics but NOT compared: cross-env rendering of
type annotations differs (`List[GraphNode]` vs fully-qualified forms),
so signature-string equality would false-negative on compatible pairs.
Known edge (documented, accepted): a surface method absorbing protocol
params via `**kwargs` registers a param mismatch — no current protocol
needs it.

A capability whose manifest has NO recorded surface (pre-fracture) is
NOT compatible — regenerate the manifest. Staleness stays visible
instead of silently mis-answering compatibility queries (the manager’s
`structural_surface_drift` flag covers the stale-recording case).

------------------------------------------------------------------------

### AdapterManifest

``` python

def AdapterManifest(
    name:str, version:str, task_name:str, module:str, class_name:str, required_tool_protocol:str,
    protocol_members:Dict=<factory>, conda_env:str='', generated_at:str='', unit:str='adapter'
)->None:

```

*A discovered ADAPTER unit (CR-17 pt 2) — the registration record for
one* task-adapter implementation installed in some tool’s worker env.

Generated in-env by `cjm-ctl generate-adapter-manifest` (the protocol
members are introspected where the protocol is importable); discovered
host-side beside capability manifests via the `unit` discriminator.

------------------------------------------------------------------------

### adapter_manifest_from_dict

``` python

def adapter_manifest_from_dict(
    d:Dict, # On-disk JSON dict (the "class" key maps to class_name)
)->AdapterManifest: # Typed adapter manifest

```

*Reconstruct an `AdapterManifest` from its on-disk JSON shape.*

------------------------------------------------------------------------

### is_adapter_manifest

``` python

def is_adapter_manifest(
    data:Any, # Raw JSON-decoded manifest content
)->bool: # True when the payload declares the adapter unit kind

```

*Route a manifest file by the `unit` discriminator (capability
manifests* carry no `unit` key; checked BEFORE `load_manifest` parsing).

------------------------------------------------------------------------

### match_protocol_against_surface

``` python

def match_protocol_against_surface(
    protocol_members:Dict, # {"methods": [...], "properties": [...]} from the adapter manifest
    structural_surface:Optional, # Capability manifest code.structural_surface (None = pre-fracture)
)->Dict: # {"compatible", "missing_methods", "missing_properties", "param_mismatches", "reason"}

```

*Surface-based compatibility (pass-2 Thread 3) — host-side,
manifest-vs-* manifest, safe against UNLOADED capabilities.

Method rule: same name present + (when both sides record params) the
surface params must START WITH the protocol params (prefix rule).
Property rule: name present in the surface’s properties. No surface
recorded -\> NOT compatible, with the reason spelled out.

``` python
# Matcher contract tests — including THE negative check (a mismatched
# pairing must say no, legibly).
_surface = {
    "methods": [
        {"name": "add_nodes", "signature": "(self, nodes)", "params": ["nodes"]},
        {"name": "query_nodes", "signature": "(self, query)", "params": ["query"]},
        {"name": "get_context", "signature": "(self, node_id, depth=1, filter_labels=None)",
         "params": ["node_id", "depth", "filter_labels"]},
    ],
    "properties": ["name", "version"],
    "attributes": [],
}

# positive: exact + prefix (extra trailing surface params fine) + property
_proto_ok = {
    "methods": [
        {"name": "add_nodes", "params": ["nodes"]},
        {"name": "get_context", "params": ["node_id", "depth"]},
    ],
    "properties": ["name"],
}
v = match_protocol_against_surface(_proto_ok, _surface)
assert v["compatible"], v

# negative: missing method (the CR-17 negative check)
_proto_missing = {"methods": [{"name": "transcribe", "params": ["audio"]}], "properties": []}
v = match_protocol_against_surface(_proto_missing, _surface)
assert not v["compatible"] and v["missing_methods"] == ["transcribe"], v

# negative: reordered / wrong params
_proto_params = {"methods": [{"name": "get_context", "params": ["depth", "node_id"]}],
                 "properties": []}
v = match_protocol_against_surface(_proto_params, _surface)
assert not v["compatible"] and v["param_mismatches"][0]["method"] == "get_context", v

# negative: missing property
v = match_protocol_against_surface({"methods": [], "properties": ["task_count"]}, _surface)
assert not v["compatible"] and v["missing_properties"] == ["task_count"], v

# pre-fracture surface (None) -> not compatible, reason spelled out
v = match_protocol_against_surface(_proto_ok, None)
assert not v["compatible"] and "structural_surface" in v["reason"], v

# param-less old-format surface entries fall back to name-only matching
_old_surface = {"methods": [{"name": "add_nodes", "signature": "(...)"}], "properties": []}
v = match_protocol_against_surface({"methods": [{"name": "add_nodes", "params": ["nodes"]}],
                                    "properties": []}, _old_surface)
assert v["compatible"], v

# manifest round-trip (the on-disk "class" key)
am = AdapterManifest(
    name="cjm_graph_storage_adapter_interface.generic.GenericGraphStorageAdapter",
    version="0.0.1", task_name="graph-storage",
    module="cjm_graph_storage_adapter_interface.generic",
    class_name="GenericGraphStorageAdapter",
    required_tool_protocol="cjm_graph_storage_adapter_interface.adapter.GraphStorageToolProtocol",
    protocol_members=_proto_ok,
)
d = am.to_dict()
assert is_adapter_manifest(d) and d["class"] == "GenericGraphStorageAdapter"
assert adapter_manifest_from_dict(d) == am
assert not is_adapter_manifest({"name": "x"})  # capability manifests lack "unit"
```
