# Task Adapter


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

## TaskAdapter

Layer-1 of the fractured capability unit is a PAIR: a **tool
capability** (`core.capability.ToolCapability` — manage the tool) + a
**task adapter** (this base — give one task a typed contract against
that tool). The two are separate registered units composed at runtime;
co-packaging in one library is authoring convenience, never fusing.

- The per-task **adapter interface library**
  (`cjm-<task>-adapter-interface`) subclasses this with the typed task
  method (e.g. `transcribe(audio, ...) -> TranscriptionResult`), the
  task’s result DTOs (wire-registered via `core.wire`), and the task’s
  persistence helpers (`save_with_logging` / `get_cached` storage
  classes).
- Adapter **implementations run IN-WORKER**, co-located with their tool
  capability (the tool-touching work is in-worker anyway: models on GPU,
  API secrets pinned per CR-12 `WORKER_ENV`).
- `required_tool_protocol` names the structural contract the adapter
  needs from a tool (a `typing.Protocol` defined in the dep-light
  interface lib). The substrate matches it against each capability’s
  recorded structural surface (manifest `code.structural_surface`) —
  host-side, against UNLOADED capabilities (surface-based,
  adapter-driven compatibility).
- **Composition logic is explicitly NOT an adapter**: code that spans
  multiple capability outputs or assembles/projects graph data is
  layer-2 composition (CR-16 ports + CR-18 graph-aware layer) and lives
  in the workflow cores.

Adapter registry + composition routing land with CR-17 pt 2 (execution
stage 4); this base fixes the SHAPE so the first adapter-interface
libraries can be born against it.

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

### TaskAdapter

``` python

def TaskAdapter(
    args:VAR_POSITIONAL, kwargs:VAR_KEYWORD
):

```

*Base for task adapters — the typed-task half of the capability-unit*
fracture (pass-2 Thread 3).

Subclasses (one ABC per task, in `cjm-<task>-adapter-interface`
libraries) declare:

- the TYPED task method (the contract `execute(*args, **kwargs)` never
  gave the task), abstract on the per-task ABC;
- `task_name`: the task this adapter serves (e.g. “transcription”);
- `required_tool_protocol`: the structural contract required of a tool
  capability (a `typing.Protocol`; provisional `None` until the protocol
  is evidence-locked — Q5 posture: declare the slot, let stage-4/8
  tool-splitting evidence finalize the protocol bodies);
- the task’s persistence helpers (storage classes), beside the task
  method rather than on it.

Implementations run in-worker beside their tool capability. The base is
deliberately mechanism-light: registry/routing is CR-17 pt 2 (stage 4).

``` python
# Shape test: a per-task ABC subclasses TaskAdapter with a typed method and
# declares the two ClassVars; a concrete impl fills them in.
from abc import abstractmethod
from typing import Protocol, runtime_checkable


@runtime_checkable
class _EchoToolProtocol(Protocol):
    def echo_native(self, text: str) -> str: ...


class _EchoAdapter(TaskAdapter):
    task_name = "echo"
    required_tool_protocol = _EchoToolProtocol

    @abstractmethod
    def echo(self, text: str) -> str: ...


class _EchoImpl(_EchoAdapter):
    def echo(self, text: str) -> str:
        return text


# The per-task ABC keeps its abstract set (ABCMeta freezes at class creation
# — the NB-1 hazard the fracture had to get right up front).
try:
    _EchoAdapter()  # type: ignore[abstract]
    raise AssertionError("abstract _EchoAdapter must not instantiate")
except TypeError:
    pass

impl = _EchoImpl()
assert impl.echo("hi") == "hi"
assert _EchoImpl.task_name == "echo"
assert _EchoImpl.required_tool_protocol is _EchoToolProtocol
assert TaskAdapter.required_tool_protocol is None  # provisional default
print("TaskAdapter shape test OK")
```

    TaskAdapter shape test OK
