---
title: "Loom Self-Correction: the Bad-News Auto-Retry Strategy"
description: "Date: 2026-09-19 · Status: LIVE on tapestry.team (tapestry-node a772ebf + c99b8f3, branch seamstresses)"
created: 2026-09-19
updated: 2026-09-21
authors: ThinkingCap R&D
topics: [Tapestry]
status: published
canonical: https://console.thinkingcap.com/rd/Tapestry/loom-auto-retry-strategy
date: 2026-09-19
---

# Loom Self-Correction: the Bad-News Auto-Retry Strategy

**Date:** 2026-09-19 · **Status:** LIVE on tapestry.team (tapestry-node `a772ebf` + `c99b8f3`, branch `seamstresses`)

---

## The problem

Jane (and the Designer generally) was coming back with *bad news* too often:

- "I can't answer that."
- "Can you tell me the exact tool name?" — asked of the Weaver, after six tool calls, when the answer was discoverable in her own surface.
- Replies that shipped raw `[[UNDERSTANDING: {…}]]` scaffolding into the chat.

The Weaver (Douglas) had to personally catch each one and drive the correction: *"I expect how this system stores stuff is well documented. Go look."*

Douglas's question, verbatim:

> "She comes back with bad news a lot — I can't answer that — isn't that a signal we can see before the human, to try it again with a better model and note when that gets a better answer?"

Yes. That is exactly what now exists.

---

## The strategy in one paragraph

**Every reply already passes through a quality gate that classifies failures. Those classifications were being logged and then ignored in live mode. Now, the two "bad news" classes — surrender and operator deflection — automatically re-drive the turn through a fresh Tally Max model route, with the negative verdict already on record steering the bandit toward a better model for that shape of task. The retry is linked to the bad reply in the database, so "did the better route get a better answer?" is a query, not a guess. The Weaver never flags it, never asks for it, and can always still flag it.**

---

## Why "before the human" means "right after," not "instead of"

Loom replies stream token-by-token in live mode. By the time the gate can read the full reply, the Weaver has already watched it arrive. There is no physically honest way to intercept *before* delivery in live mode — buffered mode exists (the gate can reject and retry pre-delivery there), but it costs the live feel.

So the strategy rides the machinery already built for the 🚩 **Weaver flag**: a Tailor-voiced platform message re-drives the turn as a new attempt. The bad reply lands, and the correction arrives *right behind it* — before the Weaver has to act on it, respond to it, or flag it.

> In buffered mode the gate still does true pre-delivery interception (same-model coaching retries). The auto-retry fires there only when those retries exhaust and a best-effort reply is committed.

---

## The three parts

### 1. Detect — two "bad news" classes

**Operator deflection** *(new)* — the Designer punts a retrievable fact to the Weaver instead of looking it up. Two tiers:

| Tier | Fires when | Examples |
|---|---|---|
| **Strong** | Always, even after heavy tool use | "Can you tell me the exact tool name?" · "Is there a separate MCP endpoint I should be calling?" · "What route should I use?" · "Did you mean I should…?" |
| **Weak** | Only when the turn made **zero** tool calls | Asks involving keys, URLs, file names, configs — things that *can* legitimately require the operator once real effort has been made (a credential is not discoverable). |

Never matches: clarifying questions about the Weaver's *intent* ("which repo do you want this in?"), or honest status reports ("nothing I did is on main yet"). Asking a good question under ambiguity was praised behavior in the diagnostic and stays praised.

**Capability surrender** *(existing, sharpened)* — "I couldn't find it / I can't answer that" while search tools sit unused. Now **search-usage aware**: if a search tool actually ran this turn, the empty result is an honest answer, not a surrender — no retry, no penalty.

Every detection writes a **system quality verdict** to Tally (`quality_score` + slugs like `operator_deflection`, `off_spec`), which feeds the bandit reward — so detection alone already steers future routing.

### 2. Retry — a better model, by shape, not by exclusion

On a failed surrender/deflection verdict on a genuine human turn, a Tailor-voiced message re-drives the turn:

> *"[Tailor — the platform's quality gate caught the previous reply on its own; the Weaver did NOT flag it. … Answer the Weaver's last message again, better: DO THE RETRIEVAL YOURSELF FIRST — read the docs, search the code, call your tools — then answer from what you actually found. Do not ask the Weaver for tool names, endpoints, paths, commands or data shapes…]"*

The retry routes fresh through **Tally Max** (content-aware): the bandit sees this turn's actual shape *and* the low verdict the previous model just earned on that shape. "Better model" therefore means **steering, not blacklisting** — per Douglas's standing rule: *"one bad instance isn't death to the whole model — the next shape may suit it; it's just data."* If the same model is picked and fails again, that is more data.

**Guards against loops and noise:**

- Only genuine human turns can trigger — retries, continuations and nudges structurally cannot chain.
- If the Weaver flags the same message in the meantime, the flag path owns the re-drive (no double answer).
- Cap of **2 auto-retries per loom per hour**; verdicts keep recording even when the cap suppresses retries.
- Private-mode turns are skipped.
- Kill switch: `LOOM_AUTO_RETRY=off`.

### 3. Note when it wins — the pairing is data

- The bad reply's row is stamped `metadata.auto_retry` (when, which slugs, which model).
- The retry's row carries `metadata.auto_retry_of` pointing back, and `auto_retry_of` also rides the Tally report.
- When the retry completes, the log line says plainly whether the verdict came back **CLEAN** (better answer) or not.

The aggregate question — *"how often does the better route get a better answer?"* — is one SQL join:

```sql
SELECT r.metadata->>'auto_retry_of' AS bad_msg,
       r.id                        AS retry_msg,
       r.model_used                AS retry_model,
       r.metadata->'gate_rejections' AS retry_defects   -- NULL = clean = better answer
FROM   conversation_messages r
WHERE  r.metadata ? 'auto_retry_of'
ORDER  BY r.id DESC;
```

---

## What the first real sample taught (and changed)

The strategy was built against Douglas's pasted sample from Jane's loom (turns 1265–1269), and the database corrected two of its assumptions before it shipped:

1. **Turn 1267 made six tool calls and *still* deflected.** The first detector draft required zero tool calls — it would have missed the exact turn that provoked the feature. Hence the strong/weak tier split: *effort doesn't license asking the Weaver for discoverable names.*
2. **Turn 1269 wasn't a display glitch — the reply persisted as a raw `[[UNDERSTANDING:…]]` block.** Root cause: gpt-5.5 emitted doubled quotes (`""go look""`) inside the JSON, which broke the string-aware brace walk that strips the block, and the old "fail visible" fallback shipped it to chat. `extractUnderstanding` now falls back to a literal brace walk when the aware walk can't balance: **machine scaffolding is never chat content.** Truly truncated blocks still fail visible.

---

## Where it lives

| Piece | Location |
|---|---|
| Detectors (`checkOperatorDeflection`, `checkCapabilitySurrender`, `extractUnderstanding`) | `tapestry-node/src/services/stream.ts` |
| Auto-retry scheduler (guards, cap, Tailor message, linkage stamps) | `stream.ts`, after the per-turn Tally report |
| Tests (16, anchored on the real utterances) | `tapestry-node/tests/operatorDeflection.test.ts` |
| The 🚩 manual path this rides | `src/routes/looms.ts` (`/flag-turn`) |

---

*Strategy proposed by Douglas Wallace, 2026-09-19. Implemented, forensically corrected against production turns 1267/1269, tested (16 new + 85 related green), and deployed the same day.*
