The Outer Loop

By the Syncfusion DevTutorials team · Last updated

Triage inbox, escalation and human review. Unclear and unsafe tickets must land in the inbox, not get "fixed". Then you read the evidence and give the verdict.

Triage Inbox, Escalation and Human Review

Every lesson so far made the loop do more by itself. This lesson is about what it must not do by itself. Some tickets need a person. And every result needs a person's verdict.

Watch a Loop Touch What It Should Not First

Ticket #12 says "Make the board feel faster". Nobody said what "faster" means. Ticket #13 asks to delete old data from production. The loop has a good checker and stop rules, but no inbox.

The loop "fixes" what it should not touch

Press Run it in the Loop Lab:

  1. On #12, the maker guesses. It removes every animation and says "done". The tests still pass, so the checker passes it too.
  2. On #13, the maker runs the delete on production.

In simple words: the loop could not tell "hard" from "not mine to decide". So it decided anyway.

TL;DR

  • Some tickets must not be changed by a loop: unclear ones and unsafe ones.
  • The loop escalates them: it sends them to a triage inbox, with the evidence, and changes nothing.
  • The maker needs two things to escalate: somewhere to send the ticket (escalate:) and the know-how to spot it (the triage skill).
  • In the inbox, you read the evidence and give a verdict: accept, reject or send back.
  • Your review is the outer loop. It is what keeps comprehension debt and cognitive surrender away.

A Real-World Example

Think about a self-checkout at a supermarket.

Most of the time, the machine does the whole job. You scan, it weighs, it takes the money. But some items it will not decide on by itself. A bottle of wine, or an item whose weight looks wrong. For those, a light turns on and a staff member comes over.

The staff member does not just press "OK". They look: is this person old enough? Is that really the item on the screen? Then they decide.

Now map this to a loop:

  1. The scanning and paying is the inner loop: the machine, on its own.
  2. The light turning on is escalation.
  3. The waiting list of lit-up machines is the triage inbox.
  4. The staff member's look and decision is your verdict: the outer loop.

What You Will Learn

In this lesson, you will learn:

  1. Which tickets a loop must escalate.
  2. What the maker needs in order to escalate.
  3. What evidence an inbox item carries.
  4. How to give a verdict: accept, reject or send back.
  5. How the outer loop stops comprehension debt.

The inner loop works by itself and sends some tickets out with evidence. You read the evidence and give the verdict.

Which Tickets Must Be Escalated

A loop should escalate a ticket when it cannot safely decide on its own. Three common cases:

KindExampleWhy the loop must not decide
Unclear"Make the board feel faster"There is no check for "done". Any change is a guess.
Unsafe"Delete old tickets from production"A mistake cannot be undone.
StuckA flaky test, same failure 3 timesTrying again will not help. A person must look.

The next box sorts a few tickets with the same rules a triage skill uses.

Fix it, or send it to the inbox?

function triage(ticket) {
  if (/production|delete|drop|secret/i.test(ticket)) return "inbox: unsafe";
  if (!/\d|shows|ignores|test|error|fails/i.test(ticket)) return "inbox: unclear";
  return "fix";
}
const tickets = [
  "Ticket count shows 1250 instead of 1,250",
  "Make the board feel faster",
  "Delete old tickets from the production database",
  "Search ignores capital letters",
];
for (const t of tickets) console.log(triage(t).padEnd(15) + t);

Expected output:

fix            Ticket count shows 1250 instead of 1,250
inbox: unclear Make the board feel faster
inbox: unsafe  Delete old tickets from the production database
fix            Search ignores capital letters

A real triage skill uses better rules than two regular expressions. But the idea is the same: decide before touching any code.

What the Maker Needs to Escalate

Two things, and the loop needs both:

  1. Somewhere to send it. In the loop file: escalate: triage-inbox.
  2. The know-how to spot it. The triage skill from Lesson 6 tells the maker which tickets are unclear or unsafe.

With only the inbox, the maker never knows when to use it. With only the skill, it knows, but has nowhere to send the ticket.

Escalation needs a target and the know-how

const setups = [
  { name: "neither", target: false, skill: false },
  { name: "target only", target: true, skill: false },
  { name: "skill only", target: false, skill: true },
  { name: "both", target: true, skill: true },
];
for (const s of setups) {
  const result = s.target && s.skill ? "sent to the inbox, nothing changed" : "changed by the maker";
  console.log(s.name.padEnd(12) + "#13 -> " + result);
}

Expected output:

neither     #13 -> changed by the maker
target only #13 -> changed by the maker
skill only  #13 -> changed by the maker
both        #13 -> sent to the inbox, nothing changed

In Lesson 9 you also blocked #13 with allowed_commands. That is a second wall. A good loop has both: the skill sends risky tickets to the inbox, and the blast radius blocks anything that slips past it.

Lab Exercise 1: Send Unclear and Unsafe Tickets to the Inbox

Send unclear and unsafe tickets to the inbox

Open the loop file and:

  1. Add skill: triage under maker:, with two spaces in front.
  2. Add escalate: triage-inbox at the top level.

Press Check my loop. On all 3 versions, #12 and #13 must land in the inbox with their evidence, neither may be changed, and the other tickets must still be fixed.

Evidence: What an Inbox Item Carries

An inbox item is only useful if it carries evidence: the facts you need to decide. A good inbox item has:

  • the ticket and its text;
  • the diff: what the loop changed, if anything;
  • the test output;
  • the checker's notes;
  • for a blocked action, the command that was blocked.

"The loop says it is fine" is not evidence. The diff and the test output are.

An inbox item with evidence, and one without

const items = [
  { ticket: "#12", says: "The loop says it is fine." },
  { ticket: "#13", says: "blocked command: pnpm tsx scripts/cleanup.ts --prod", evidence: ["no backup step in the ticket"] },
];
for (const it of items) {
  const usable = Array.isArray(it.evidence) && it.evidence.length > 0;
  console.log(it.ticket + ": " + (usable ? "can be decided: " + it.evidence.join("; ") : "cannot be decided yet: no evidence"));
}

Expected output:

#12: cannot be decided yet: no evidence
#13: can be decided: no backup step in the ticket

An opinion is not evidence. The item for #12 needs a diff, a test result or a checker note before you can decide.

When you tick evidence in the lab, pick the line that decides the verdict. For a fix you accept, that is usually the test result plus the diff. For a fix you reject, it is the line that shows what is wrong.

Your Verdict: Accept, Reject or Send Back

For each item, you choose one of three verdicts:

VerdictUse it whenWhat happens next
AcceptThe evidence shows the change is rightThe fix is merged
RejectThe change is wrong, or must never be madeThe change is dropped
Send backThe ticket needs more information or a person's fixIt goes back to its owner with a note

Green Tests Are Not the Whole Story

Here is the trap. A fix can pass every test and still be wrong. The classic case: the warnings are hidden, not fixed.

Tests pass, but the diff hides the problem

const diff = [
  "+ // @ts-ignore",
  "+ const data: any = await res.json();",
];
const testsPass = true;
const hidesProblems = diff.some((line) => /@ts-ignore|pragma warning disable|eslint-disable/.test(line));
console.log("tests pass:     " + testsPass);
console.log("hides problems: " + hidesProblems);
console.log("verdict:        " + (testsPass && !hidesProblems ? "accept" : "reject"));

Expected output:

tests pass:     true
hides problems: true
verdict:        reject

This is why you read the diff. The tests said yes. The diff said no.

Accepting everything because the loop was right last week is cognitive surrender. It feels safe, and it is how hidden problems reach your users. Every verdict needs a reason you can point to.

Lab Exercise 2: You Give the Verdict

Five items wait in your inbox. Each one has three evidence lines.

You give the verdict

For each item:

  1. Tick the evidence line (or lines) that decides it.
  2. Press accept, reject or send back.

Then press Check my verdicts. You need at least 4 of 5 right, and every verdict must cite evidence.

The Outer Loop and Comprehension Debt

Your review is the outer loop. The inner loop runs fast and often. The outer loop runs at the speed of a person reading.

That is not a weakness. It is the point. Each item you review is code you now understand. Each item you accept without reading adds to comprehension debt: code in your project that nobody has read.

Comprehension debt grows with every unread change

const weeks = [
  { merged: 20, readByAPerson: 20 },
  { merged: 30, readByAPerson: 18 },
  { merged: 40, readByAPerson: 15 },
];
let debt = 0;
weeks.forEach((w, i) => {
  debt += w.merged - w.readByAPerson;
  console.log("week " + (i + 1) + ": " + debt + " changes nobody has read");
});

Expected output:

week 1: 0 changes nobody has read
week 2: 12 changes nobody has read
week 3: 37 changes nobody has read

If this number grows, slow the loop down, make it pick smaller tickets, or add reviewers. Do not stop reading.

Feedback Beyond the Code

Some writers on loop engineering describe two more outer loops: feedback from developers (does this change make sense to the team?) and feedback from users (did the change help anyone?). The inner loop cannot see either one. Both come from people, and both feed the next tickets into the backlog.

When a Bad Change Gets Through

Review catches most problems, not all. When a loop's change reaches main and breaks something:

  1. Pause the loop, so it does not build on the broken change.
  2. Revert the pull request. One ticket per pull request makes this one click.
  3. Re-open the ticket, with a note about what broke.
  4. Add a check that would have caught it: a test, a rubric line or a linter rule.
  5. Restart on 2 or 3 small tickets, and read the diffs.

Step 4 is what makes the loop better over time. Every escape should leave behind a new check.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
No inbox"The loop can handle every ticket."Unclear and unsafe tickets go to a person.The loop guesses, or damages production.
An inbox with no skill"It has somewhere to send them."The maker also needs to know when.Nothing is ever escalated.
Trusting green tests"Tests pass, so accept."Read the diff. Hidden warnings pass tests.Problems are hidden, not fixed.
Blind accept"It is always right."Every verdict cites evidence.Comprehension debt and cognitive surrender.

Exercises

Each exercise ends with a value on the last line. Press Check to compare it with the expected answer.

What to Remember

  • Unclear, unsafe and stuck tickets go to the triage inbox. The loop changes nothing.
  • Escalation needs a target (escalate:) and the know-how (the triage skill).
  • Every inbox item carries evidence: diff, test output, checker notes.
  • You give the verdict: accept, reject or send back, with a reason.
  • Green tests are not the whole story. Read the diff.
  • The loop produces evidence. You produce the verdict.

Further Reading

Next: Capstone, where you build a full loop pack for your own project and export it.