# TRINITY THIN CLIENT MIGRATION — CRITICAL FIXES

## Context You Don't Have

On 2026-02-13, Trinity was migrated from lockstock-integrations 1.x (daemon/socket)
to 2.0.0rc3 (thin client/API). The sed substitutions were mechanically correct but
introduced three silent security regressions. Nobody caught them during the migration
because the middleware initializes without errors and request stamping works fine.
The problems are all in what the middleware quietly stops doing.

**The file to edit on RunPod:** `/workspace/trinity_openai_adapter_lockstock.py`
**The middleware init block is around line 442** (search for `LockStockMiddleware`).

---

## Fix 1: Add response_tasks Dictionary

### What's wrong

The middleware init has `path_tasks` mapping routes to stamp task names, but
`response_tasks` was missing. Without it, the middleware intercepts outgoing LLM
responses, reaches `self.response_tasks.get(task)`, gets `None`, and silently
skips response stamping. The `_fire_and_forget_response_stamp()` method exists
and works — it just never fires.

### What this means

Trinity's chain records every question asked but nothing Trinity answers.
The cryptographic audit trail has no record of what the AI actually said or did.

### The fix

Add `response_tasks` to the `LockStockMiddleware` constructor call. The dictionary
maps each request task name to its corresponding response task name. Every key in
`path_tasks` that represents an LLM call should have a response counterpart.

Find the middleware init block. It should look approximately like this:

```python
app.add_middleware(
    LockStockMiddleware,
    api_key=API_KEY,
    agent_id=AGENT_ID,
    server_url=SERVER_URL,
    path_tasks={
        "/v1/chat/completions": "CHATCOMPLETION",
        # ... other routes
    },
    # response_tasks may be MISSING — add it here
    block_on_rejection=False,
    timeout=10.0,
)
```

Add the `response_tasks` parameter:

```python
    response_tasks={
        "CHATCOMPLETION": "CHATCOMPLETIONRESPONSE",
        # Mirror every LLM-related entry in path_tasks:
        # The pattern is: "TASKNAME": "TASKNAMERESPONSE"
    },
```

**NOTE:** This fix was already applied on 2026-02-13. Verify it's present.

### How to verify

After the change, make a test LLM call through Trinity. Check the LockStock
dashboard or chain explorer. You should see **TWO stamps per call**: one for the
request (e.g., CHATCOMPLETION) and one for the response (e.g., CHATCOMPLETIONRESPONSE).
If you only see request stamps, `response_tasks` is still not wired correctly.

---

## Fix 2: Set block_on_rejection=True

### What's wrong

The migration changed `block_on_failure=True` to `block_on_rejection=False`.
This looks like a parameter rename but it is a **behavioral inversion**.

In the old daemon middleware, `block_on_failure=True` meant: if stamping fails
for ANY reason (server down, timeout, DNS failure, explicit rejection, expired
token, changed fingerprint), the LLM request does not proceed. **No audit = no action.**

In the thin client, `block_on_rejection` covers the same failure scope (connection
errors return `authorized=False` via the `except httpx.RequestError` handler in
`thin.py` around line 195). But it was set to `False`, meaning Trinity proceeds
unstamped regardless of why stamping failed.

### What this means

Trinity operates with **zero accountability** if the chain server is unreachable,
the genesis token expires, the fingerprint changes, or authorization is revoked.
The system meant to enforce cryptographic accountability becomes best-effort telemetry.

### The fix

Find this line in the middleware init:

```python
    block_on_rejection=False,  # Reject requests if chain auth fails
```

Change it to:

```python
    block_on_rejection=True,  # FAIL CLOSED: no stamp = no LLM action
```

### The comment matters

The old comment "Reject requests if chain auth fails" was a fossil from the sed
replacement — it described the OLD `block_on_failure=True` behavior while the
new code did the opposite. The replacement comment must accurately describe
what `True` does: Trinity will refuse to proxy LLM requests if it cannot stamp
them on chain.

---

## Fix 3: Update the adapter version string (if not already done)

Confirm this line exists in the middleware init kwargs or in the health endpoint:

```python
    "adapter": "trinity-openai-lockstock-v2",
```

If it still says `"trinity-openai-lockstock"` (without `-v2`), update it.
This distinguishes thin-client stamps from any legacy daemon stamps on the chain.

---

## Fix 4: Verify the status/health endpoint

The status endpoint (search for `/lockstock/status` in the adapter file) should report:

```python
    "api_key": "***",          # NOT "socket_path"
    "stamped_routes": count,   # The counter tracking stamped requests
```

If it still references `socket_path` or the old daemon fields, update it.

---

## Verification Checklist After All Fixes

1. Start Trinity with the updated adapter
2. Confirm middleware initializes without errors in the startup log
3. Make one LLM chat completion call through the gateway
4. Check chain explorer for **TWO stamps**: `CHATCOMPLETION` + `CHATCOMPLETIONRESPONSE`
5. Stop the LockStock API server (or block its port)
6. Make another LLM call — it should **FAIL** with a stamping error, NOT proceed
7. Restart the LockStock API server
8. Confirm normal stamping resumes
9. Hit the `/health` or `/lockstock/status` endpoint and confirm it shows `api_key` and `stamped_routes`

**If step 4 shows only one stamp:** `response_tasks` is wrong.
**If step 6 allows the LLM call through:** `block_on_rejection` is still `False`.

---

## Why This Matters

The daemon architecture made these guarantees **implicit** — they were properties of
the system, not configuration options. The thin client turned every guarantee into
a parameter with a permissive default. The library defaults to fail-open because
that's safest for general-purpose web apps.

**Trinity is not a general-purpose web app.**

Trinity is an AI agent whose entire trust model depends on the chain recording
everything it does and preventing it from acting unaccountably. These fixes
restore the daemon's security posture in the thin client's architecture.

---

## The Architectural Lesson

Three things moved from implicit to explicit in the thin client migration:

| Guarantee | Daemon (v1) | Thin Client (v2, pre-fix) | Thin Client (v2, post-fix) |
|-----------|-------------|--------------------------|---------------------------|
| Response stamping | Automatic | Required `response_tasks` dict | Auto-inferred from `path_tasks` (default) |
| Fail-closed enforcement | Architectural | Required `block_on_rejection=True` | Default is `True` (fail-closed) |
| Hardware binding | TPM/daemon-level | Fingerprint in `fingerprint.py` | Fingerprint in `fingerprint.py` |

The library defaults were fixed on 2026-02-14 to match the daemon's security
posture. Response stamping is auto-inferred and fail-closed is the default.
Hardware binding remains the only guarantee that is purely code-level.

---

## Resolution (2026-02-14)

All four fixes are complete:

1. **response_tasks**: Auto-inferred by library default. Trinity adapter also passes explicitly.
2. **block_on_rejection=True**: Library default. Trinity adapter also passes explicitly.
3. **Adapter version string**: Updated to v2.
4. **Status endpoint**: Updated, no daemon references.

### Trinity Adapter
- File: `/workspace/trinity_openai_adapter_thin.py` (clean rewrite, 738 lines)
- Old hybrid backed up: `/workspace/trinity_openai_adapter_lockstock.py.bak.pre-thin-migration`
- 16 stamped routes, 2 excluded, startup probe with 3 retries
- Dashboard verified: sequential dual stamps (seq 12-17)

### Library Defaults Fixed
- `response_tasks=None` now auto-infers `TASK+"RESPONSE"` from `path_tasks`
- `block_on_rejection` now defaults to `True`
- Three-way semantics: `None`=auto-infer, explicit dict=use as-is, `{}`=opt-out with warning
- 25/25 tests passing

### What This Means for Future Migrators
A developer doing the mechanical swap (change import, swap socket for api_key)
now gets dual stamping and fail-closed behavior without knowing they need it.
The failure that took two days to diagnose on Trinity is now impossible to
reproduce via the default configuration path.

---

## SSH Access to RunPod

```bash
ssh -p 18931 -i ~/.ssh/id_ed25519 root@216.81.245.127
```

The adapter file is at `/workspace/trinity_openai_adapter_thin.py`.

Restart after changes:
```bash
/workspace/start_trinity_with_lockstock.sh
```
