Skip to content
AI Interfaces block

Human Oversight Split

Where a person sits in an automated run, written as four guarantees rather than as a promise that a human is involved.

156 linesNo dependenciesAdded 9 Sept 2026
  • oversight
  • human
  • agent
  • guardrails
  • trust

What's included

  • components/human-oversight-split.tsx
  • No runtime dependencies

Works with

  • React
  • Next.js
  • Tailwind CSS
  • TypeScript

npx hoverlab add human-oversight-split

Or over MCP, from your editor's agent — no account needed.

License

Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.

Was this useful?

Start a page with this section — add more, order them, and leave with the page source.

Preview

Oversight

Where the person actually is

"A human is in the loop" is a claim with no shape. These are the four points where one really is, and each is a thing the system will not do without them.

  • Before anything leavesExternal messages and payments stop and wait. The full payload is shown, not a summary of it.
  • On low confidenceThe run pauses rather than picking the best of four bad options and presenting it as an answer.
  • After the fact, in the logEvery unattended action is recorded with its inputs, and the log is exportable.
  • The stop buttonOne press halts a run mid-step and rolls back anything reversible. It is always on screen.

Rendered live in your current theme — this is the same component whose source is below, not a screenshot of it.

Source

components/human-oversight-split.tsx
/**
 * <HumanOversightSplit> — Where a person sits in an automated run, written as four guarantees rather than as a promise that a human is involved.
 *
 * "A human is in the loop" is a claim with no shape, and the layout problem
 * is turning it into something falsifiable. The obvious wrong answer is a
 * paragraph about responsible AI, which commits to nothing and is
 * indistinguishable from every other such paragraph.
 *
 * Four points, each naming a place where the system will not proceed
 * without a person. Written as guarantees rather than as intentions,
 * because a guarantee can be checked against the product and an intention
 * cannot.
 *
 * The second point — pausing on low confidence — is the one that separates
 * real oversight from theatre. A system that always produces an answer has
 * no low-confidence branch, and the human in its loop is only ever
 * reviewing outputs that were going to be produced anyway.
 *
 * The fourth point is deliberately mundane. A stop button that is always on
 * screen is worth more than the three principles above it, because it is
 * the one that works when the other three have failed, and "always on
 * screen" is the part that is usually not true.
 *
 * Accessibility: the tick `<svg>` carries `aria-hidden` and
 * `fill="currentColor"`, so it inherits the text colour and is not
 * announced; the section is named by its heading through `aria-labelledby`;
 * the points are a genuine `<ul>` so a screen reader announces four items
 * before reading them, which is what makes a list of guarantees scannable
 * by ear.
 *
 * The panel is drawn rather than an image, so it themes correctly and adds
 * no asset. Pass `media` to show the actual approval card.
 */

import * as React from 'react'

export interface HumanOversightSplitPoint {
  label: string
  detail?: string
}

export interface HumanOversightSplitProps {
  eyebrow?: string
  heading?: string
  intro?: string
  points?: HumanOversightSplitPoint[]
  /** Your own visual. Omit for the drawn panel, which needs no asset. */
  media?: React.ReactNode
  className?: string
}

const POINTS: HumanOversightSplitPoint[] = [
  { label: "Before anything leaves", detail: "External messages and payments stop and wait. The full payload is shown, not a summary of it." },
  { label: "On low confidence", detail: "The run pauses rather than picking the best of four bad options and presenting it as an answer." },
  { label: "After the fact, in the log", detail: "Every unattended action is recorded with its inputs, and the log is exportable." },
  { label: "The stop button", detail: "One press halts a run mid-step and rolls back anything reversible. It is always on screen." },
]

/*
  Per-instance id, hashed from props that differ between instances.

  A literal id is a latent duplicate the moment this block is rendered
  twice on one document -- two pages on a catalog hub, or one page using
  the section twice. `aria-labelledby` pointing at a duplicated id resolves
  to whichever element comes first, so the second copy is announced with
  the first copy's label. Server component, so no `useId`: hashing props
  gives each instance its own target and stays stable across server and
  client renders in a way a counter would not.
*/
function instanceId(...parts: (string | undefined)[]): string {
  const text = parts.filter(Boolean).join('|')
  let hash = 0
  for (let i = 0; i < text.length; i++) hash = (Math.imul(hash, 31) + text.charCodeAt(i)) | 0
  return (hash >>> 0).toString(36).slice(0, 6)
}

export function HumanOversightSplit({
  eyebrow = "Oversight",
  heading = "Where the person actually is",
  intro = "\"A human is in the loop\" is a claim with no shape. These are the four points where one really is, and each is a thing the system will not do without them.",
  points = POINTS,
  media,
  className,
}: HumanOversightSplitProps) {
  const uid = instanceId(heading, eyebrow)

  return (
    <section
      aria-labelledby={`human-oversight-split-heading-${uid}`}
      className={`w-full bg-background px-6 py-16 sm:py-24 ${className ?? ''}`}
    >
      <div className="mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-2">
        <div>
          <p className="text-sm font-medium text-primary">{eyebrow}</p>
          <h2
            id={`human-oversight-split-heading-${uid}`}
            className="mt-2 text-3xl font-semibold tracking-tight text-foreground sm:text-4xl"
          >
            {heading}
          </h2>
          <p className="mt-4 text-base text-muted-foreground">{intro}</p>

          <ul className="mt-8 space-y-4">
            {points.map((point) => (
              <li key={point.label} className="flex gap-3">
                {/*
                  currentColor, not a token in a raw colour function. These
                  are complete oklch() values, so hsl(var(--primary)) is not
                  a colour and the declaration is dropped silently.
                */}
                <svg
                  aria-hidden="true"
                  viewBox="0 0 20 20"
                  className="mt-0.5 size-5 shrink-0 text-primary"
                  fill="currentColor"
                >
                  <path d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z" />
                </svg>
                <span>
                  <span className="block text-sm font-medium text-foreground">{point.label}</span>
                  {point.detail ? (
                    <span className="mt-0.5 block text-sm text-muted-foreground">
                      {point.detail}
                    </span>
                  ) : null}
                </span>
              </li>
            ))}
          </ul>
        </div>

        {/*
          The drawn panel rather than an <img>. No asset to host, no layout
          shift while it loads, and it themes with the rest of the page —
          which a screenshot of somebody's light-mode dashboard does not.
        */}
        <div className="rounded-xl border border-border bg-card p-6 shadow-sm">
          {media ?? (
            <div aria-hidden="true" className="space-y-3">
              <div className="h-3 w-1/3 rounded bg-primary/30 border border-transparent" />
              <div className="h-24 rounded-lg bg-muted border border-transparent" />
              <div className="grid grid-cols-3 gap-3">
                <div className="h-14 rounded-lg bg-muted border border-transparent" />
                <div className="h-14 rounded-lg bg-muted border border-transparent" />
                <div className="h-14 rounded-lg bg-muted border border-transparent" />
              </div>
              <div className="h-3 w-2/3 rounded bg-muted border border-transparent" />
              <div className="h-3 w-1/2 rounded bg-muted border border-transparent" />
            </div>
          )}
        </div>
      </div>
    </section>
  )
}

Before you paste

  • Styling is Tailwind utility classes on semantic tokens (bg-card, text-muted-foreground) — it inherits your theme instead of overriding it.
  • Nothing to install. No component library, no icon package.
  • Every prop has a default, so it renders standalone before you wire it up.

Where it goes

Drop it at components/human-oversight-split.tsx and import it where you need the section:

import { HumanOversightSplit } from '@/components/human-oversight-split'

Customize

2 of this block’s props are simple enough to drive from here. Change them and the block below re-renders — it is the same component whose source is above, not a mock of it. Everything else it accepts is in the table underneath.

Props

Read out of the component’s own type and signature, so this cannot drift from the source below. Every prop has a default — the component renders standalone before you pass it anything.

PropTypeDefault
eyebrowstring"Oversight"
headingstring"Where the person actually is"
introstring—
pointsHumanOversightSplitPoint[]POINTS
mediaYour own visual. Omit for the drawn panel, which needs no asset.React.ReactNode—
classNamestring—

Not using React?

The same block rendered once to markup, wrapped as a file your framework compiles. Tailwind classes are framework-agnostic, so the design transfers intact — the behaviour does not.

human-oversight-split.html
<!--
  Human Oversight Split — markup from the Hoverlab catalog.

  This is the block rendered once to HTML and wrapped as a component
  file. It is not a port of the React source: the Tailwind classes carry
  the design, which is the part that took the work, and they are the same
  in every framework.

  This block has no interactive behaviour, so nothing is missing.
-->
<section aria-labelledby="human-oversight-split-heading-15qnsx" class="w-full bg-background px-6 py-16 sm:py-24 ">
  <div class="mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-2">
    <div>
      <p class="text-sm font-medium text-primary">Oversight</p>
      <h2 id="human-oversight-split-heading-15qnsx" class="mt-2 text-3xl font-semibold tracking-tight text-foreground sm:text-4xl">Where the person actually is</h2>
      <p class="mt-4 text-base text-muted-foreground">&quot;A human is in the loop&quot; is a claim with no shape. These are the four points where one really is, and each is a thing the system will not do without them.</p>
      <ul class="mt-8 space-y-4">
        <li class="flex gap-3">
          <svg aria-hidden="true" viewBox="0 0 20 20" class="mt-0.5 size-5 shrink-0 text-primary" fill="currentColor">
            <path d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z"></path>
          </svg>
          <span>
            <span class="block text-sm font-medium text-foreground">Before anything leaves</span>
            <span class="mt-0.5 block text-sm text-muted-foreground">External messages and payments stop and wait. The full payload is shown, not a summary of it.</span>
          </span>
        </li>
        <li class="flex gap-3">
          <svg aria-hidden="true" viewBox="0 0 20 20" class="mt-0.5 size-5 shrink-0 text-primary" fill="currentColor">
            <path d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z"></path>
          </svg>
          <span>
            <span class="block text-sm font-medium text-foreground">On low confidence</span>
            <span class="mt-0.5 block text-sm text-muted-foreground">The run pauses rather than picking the best of four bad options and presenting it as an answer.</span>
          </span>
        </li>
        <li class="flex gap-3">
          <svg aria-hidden="true" viewBox="0 0 20 20" class="mt-0.5 size-5 shrink-0 text-primary" fill="currentColor">
            <path d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z"></path>
          </svg>
          <span>
            <span class="block text-sm font-medium text-foreground">After the fact, in the log</span>
            <span class="mt-0.5 block text-sm text-muted-foreground">Every unattended action is recorded with its inputs, and the log is exportable.</span>
          </span>
        </li>
        <li class="flex gap-3">
          <svg aria-hidden="true" viewBox="0 0 20 20" class="mt-0.5 size-5 shrink-0 text-primary" fill="currentColor">
            <path d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L3.3 9.7a1 1 0 1 1 1.4-1.4l3.8 3.8 6.8-6.8a1 1 0 0 1 1.4 0Z"></path>
          </svg>
          <span>
            <span class="block text-sm font-medium text-foreground">The stop button</span>
            <span class="mt-0.5 block text-sm text-muted-foreground">One press halts a run mid-step and rolls back anything reversible. It is always on screen.</span>
          </span>
        </li>
      </ul>
    </div>
    <div class="rounded-xl border border-border bg-card p-6 shadow-sm">
      <div aria-hidden="true" class="space-y-3">
        <div class="h-3 w-1/3 rounded bg-primary/30 border border-transparent"></div>
        <div class="h-24 rounded-lg bg-muted border border-transparent"></div>
        <div class="grid grid-cols-3 gap-3">
          <div class="h-14 rounded-lg bg-muted border border-transparent"></div>
          <div class="h-14 rounded-lg bg-muted border border-transparent"></div>
          <div class="h-14 rounded-lg bg-muted border border-transparent"></div>
        </div>
        <div class="h-3 w-2/3 rounded bg-muted border border-transparent"></div>
        <div class="h-3 w-1/2 rounded bg-muted border border-transparent"></div>
      </div>
    </div>
  </div>
</section>
  • This is rendered HTML, not a translation of the React source. The Tailwind classes carry the design and work in any framework.
  • It is one frame: the component in its initial state, with no props applied beyond the defaults.
  • Requires Tailwind, and the design tokens the classes reference (bg-card, text-muted-foreground, and so on). The template ZIPs ship a globals.css that defines them.

What each framework gets across the whole catalog — effects convert properly; this rung is markup.

For AI

The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.

See the prompt

Used in these pages

Want the whole screen instead of this one section? Open a page and copy it entire.

7 more blocks in Human in the Loop

All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.

Browse Human in the Loop

Shipping one commercially

Copying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.

  • A commercial licence for everything in the catalog
  • Unlimited bundle exports, in Vue, Svelte and Tailwind
  • One payment — no subscription, nothing to renew
Pro — $79 once

More Human in the Loop blocks

View category
Open the full page for this block

Approval Request with Blast Radius

The agent stops and asks: the effect stated before the verb, choices as radios in a fieldset so picking is separate from committing, no pre-focused Approve, and the decision left on screen afterwards.

Human in the Loop247 lines1 dep
Open the full page for this block

Proposed Edits Diff Table

Machine-proposed row edits accepted one at a time, with before and after in real del/ins elements, a tri-state select-all that actually sets indeterminate, and a running count of what will be written.

Human in the Loop269 lines1 dep
Open the full page for this block

Recommendation with Confidence Meter

A suggestion scored with a native meter rather than a styled bar, banded in words as well as numbers, and showing what the model rejected — the fastest way a human catches a bad recommendation.

Human in the Loop258 lines1 dep
Open the full page for this block

Agent Permission Scopes

Read and write scopes separated rather than bundled, writes off by default, a required expiry, and switches that are real checkboxes with role="switch" — so the grant is toggleable by keyboard and announced as on or off.

Human in the Loop297 lines1 dep
Open the full page for this block

Bulk Approval Queue

Forty decisions and an honest way to clear them: bulk approve that cannot reach the high-impact rows, a select-all that says how many it takes, and the auto-approve deadline on every line.

Human in the Loop307 lines1 dep
Open the full page for this block

Approval Policy List

Which agent actions run unattended and which need a signature, stated as a policy the operator can read before anything happens.

Human in the Loop156 linesNo deps
Open the full page for this block

Escalation Queue

What is waiting on a person, ordered by how long it has been waiting rather than when it arrived.

Human in the Loop148 linesNo deps