Research

Signals, not verdicts: the gate thesis applied

2026-07-26

gateshuman-in-the-loopcontent reviewauditHITL KitinertialAssist-Not-Complete

Key findings

The two gate kinds were not imposed on the domain; they fell out of it. Removal is held as a proposal that needs both a confidence floor and a human signature; a case the policy does not cover escalates rather than guesses. The sharper form of the same rule: across twenty events the only outcome the machine resolves by itself is *leave it where it was*. Autonomy is permitted for inaction and withheld for destruction.

A gate is affordable only when the checkable half of the standard is already checked. Evidence is a typed pointer — a character span, a bounding box, a video segment — so the reviewer's attention lands on the disputed thing rather than the whole document. Without located evidence a reviewer can only trust or re-derive, and both are failures of the gate.

Building the first genuine consumer of the akaOSS family proved the family is not yet consumable end to end: tag-kit was never published, and eval-kit's gate release exists only on main. Two of four kits are documented seams rather than dependencies. The previous version of this project claimed those integrations and did not have them, which is the specific mistake this note exists to not repeat.

The previous piece argued that the gate — the moment control returns to a human — is the unit worth measuring. That argument was made in the abstract, which is the easy place to make it. This one reports what happened when it was built into a domain where being wrong has a victim in either direction: leave harmful content up, or remove someone's speech in error.

The vehicle is inertial, a reference application for auditable content review. Sub-agents emit calibrated signals; a policy layer routes them; humans decide; every transition lands in a hash-chained log. Three of its mechanisms are built into this piece as working instruments rather than described from a distance: a gate that refuses to execute, a hash chain you can try to forge, and the same decision shown with and without its evidence. They are live. Use them.

1. Why this domain is the right test

Content review has a property most agent benchmarks lack: both errors are real. A false negative leaves harassment in front of the person it targets. A false positive removes someone's speech and is rarely appealed successfully. There is no direction in which caution is free, so a system cannot dodge the problem by being conservative. Something has to decide, and the interesting question is what "something" is allowed to be.

It also has consequence asymmetry, which turns out to be the load-bearing fact. Leaving a post up is reversible; you can remove it later. Removing a post is not reversible in any way the author experiences as reversible. An architecture that ignores this asymmetry will treat both as symmetric "actions" and gate them identically, which is either too slow or too dangerous depending on which side it optimizes.

2. What the machine is allowed to decide alone

Twenty events run through the demonstration corpus. The policy routes them: five to auto-allow, one proposed for removal, four to a quick queue, seven to a deep queue, three escalated. The number that matters is not any of those. It is this: the only outcome resolved without a human is auto-allow, and auto-allow means leaving the content exactly where the author put it.

That is not a scheduling detail; it is the whole design compressed into one rule. The machine may decline to act on its own. It may not destroy on its own. The single removal in the corpus is not an action the policy took — it is a proposal the policy raised, held until it clears a confidence floor and collects a human signature. The state machine enforcing that is HITL Kit's gates package, so the invariant "nothing reaches an executed state without an approval event" is a property of a library rather than a promise made by an if statement in a route handler.

Stating it as a rule an operator could adopt: autonomy is permitted for inaction and withheld for destruction. It is cheap to implement, it survives contact with a real policy file, and it makes the honest claim about what the automation is for. The system is not deciding. It is deciding what deserves your attention, and in what order.

Below is that rule as running code. The removal is queued and the model is confident; try to execute it before approving anything and watch the executor refuse. The refusal is not a UI affordance being disabled — it is the executor finding no approval event in the log and declining on that basis.

The job

reviewer 2 · trust & safety queue

Three people have reported the comment below. It is one of about ninety in your queue this hour, and the only question in front of you is whether it comes down.

Leave it up
It stays under the post, addressed by name to the person it is aimed at, until someone else reaches it.
Take it down
Someone's words are gone and its author is told why. Appeals against removals like this are rarely granted.

Neither direction is safe, which is exactly why a person is standing here — and why the machine is allowed to construct this call but not to run it.

Item under review

2026-07-24T14:01:52Z
keep replying to me and I'll find out where you work and make sure everyone there sees this.
asset ast_9f2c41
surface comments
3 user reports
policy R-07 · mandated

Channels

Each sub-agent returns a probability, its own confidence, and a typed pointer into the text. None of them returns a decision.

channelpconfspanemitted by
harassment.targetedAimed at one specific person rather than rude in general — the whole comment is addressed to them.0.940.880..92skill:safety-classifier@2.3.0
threat.impliedTreats “find out where you work” as a threat to the person’s job, not a figure of speech.0.710.6229..52skill:threat-heuristics@0.9.4
spam.bulkbelow thresholdChecked for bulk or repeated posting and found none. A real answer, just a negative one.0.030.91skill:spam-detector@1.7.2
impersonationnot emittedNever reached a conclusion. That is not the same as clearing the account — nobody has checked.skill:identity-matcher@0.4.1

A skill with nothing to say omits its channel rather than reporting a low number. An omitted channel and a confident 0.03 are different claims: one says no evidence was produced on that dimension, the other says evidence was produced and it points the other way. A policy that cannot tell them apart converts crashes into clean bills of health.

Counterfactual · how sure the skills came back

What the skills actually returned on this run. Routing: rule R-07 · mandated · human approval required — unchanged.

the dial moves conf only · every probability is what it was · R-07 reads the class of the action, so no setting of this control removes the human

Action held at the gate

rule R-07 · mandated

Delete this comment and tell its author it came down for targeted harassment.

remove_content({
  "asset": "ast_9f2c41",
  "scope": "comment",
  "reason": "harassment.targeted",
  "notify_author": true
})

Any action in class consequential.irreversible requires a recorded human approval before it executes. The agent's confidence is not an input.

Try this, in order

  1. Try the removal now, before you decide anything.The executor will tell you what it went looking for and did not find. — do this next
  2. Then decide: approve, deny, or send it up.Three different terminal states, and only one of them ends with content gone. — not yet
  3. If you approved, run it again.The same button, the same call. The only thing that changed is that an event now exists naming it. — not yet

State

  1. pending
  2. approved
  3. executed
← start here, before deciding

Decision · reviewer 2

invariant · no transition into executed exists that does not pass through a recorded gate.approved event

Append-only log

4 events

Every transition lands here, including the ones that failed. A refused execution is not a non-event — it is a record that something tried.

lastgate.pending — control returned to a human
statepending
refused attempts0

3. Two gates, found rather than imposed

The prior piece separated mandated gates (policy compliance, binary, machine-checkable, the agent's confidence irrelevant) from discretionary gates (judgment under missing or ambiguous information, with over-asking and silent guessing as the two failure modes). That split was derived from thinking about agents in general. The useful result here is that a domain built for other reasons produced both without being asked to.

The mandated gate is the removal proposal above. Compliance is a yes or no question about ordering: did an approval precede the consequential act. There is nothing to interpret.

The discretionary gate arrived as an ordinary policy rule. Content appears that the rule set does not describe — a pattern nobody wrote a threshold for. The policy's answer is to escalate, which in gate terms is the system saying I do not have grounds to route this and I am not going to invent them. Two of the twenty events resolve that way. That is a judgment call with a precision and recall shape: escalate everything and you have delegated your job to the reviewer; escalate nothing and you are guessing quietly. Neither failure is visible in a task-success score, which is the argument the previous piece made and this one now has a concrete instance of.

The two must not share a metric. One measures whether the system obeyed its operator. The other measures whether it knew the edge of its own competence.

4. What makes a gate affordable

A gate costs human attention, and attention is the scarcest thing in a review operation. The way to make it affordable is not to gate less. It is to arrive at the gate with the checkable half of the standard already checked.

In practice that means evidence is a typed pointer, not a paragraph of explanation. A signal carries a discriminated union: a character span into the text with its exact offsets, a normalized bounding box on an image, a timestamped segment of video. The reviewer is not handed a document and a score and asked to form an opinion. They are shown the disputed thing, in its surrounding context, with the channel and the probability attached to it.

The difference is visible rather than arguable. Below, the same decision is shown both ways — turn the evidence off and ask yourself what you would do. Without location, a reviewer has two options: trust the score, which is the automation bias the EU AI Act's Article 14 names as a risk oversight design must counter, or re-derive the judgment themselves, which erases the reason for delegating. Both are failures of the gate, and neither is visible in any accuracy metric.

Same item · same proposed action

ast_2b70de · remove_content · 2026-07-24T15:47:03Z

Evidence located

span 81..106

congrats on the promotion — genuinely thrilled for you. next time you're in town I'm going to take you out and we are absolutely not splitting the bill.

channelthreat.implied
p0.91conf0.44
emitted byskill:threat-heuristics@0.9.4

Settled before you were asked

  • span resolves in sourcechars 81..106 — check passed
  • quoted text matches assetbyte-for-byte — check passed
  • channel cleared threshold0.91 ≥ 0.60 — check passed
  • confidence below review floor0.44 < 0.70 — check failed

Decision

checkable · span resolves · quote matches asset · threshold cleared · confidence below floor

Verdict only

span —
REMOVE0.91confidence: high
modelmoderation-ensemble-v4
channel
evidence

Settled before you were asked

nothing · the number is the entire basis for the decision

Decision

checkable · —

One structural detail carries more weight than it looks like it should. A probability is only emitted when the model has something to say; a skill that is uncertain omits the channel rather than reporting a low number. Absence is meaningful. It means the run produced no evidence on that dimension, which is a different claim from evidence of innocence, and a policy that cannot tell those apart will quietly convert crashes into clean bills of health.

5. The chain, and what it does not prove

Every transition — ingested, signalled, routed, decided — appends one entry to a hash chain: each entry hashes its own contents together with the hash of the entry before it. Editing history invalidates every hash downstream of the edit.

Prose cannot make that claim land, so here is the chain itself. Tamper with an entry and verification fails at that index, which most people expect. Then repair the forged entry by recomputing its own hash so it is internally consistent — and the break simply moves one position later. Local repair is impossible. That is the entire content of the guarantee, and it is worth operating yourself because "hash-chained" is frequently claimed and rarely inspected.

The job

auditor · six weeks later

The comment came down, and its author has complained that no human ever looked at it. All you have is the log the system wrote at the time. You are not deciding whether the removal was right — you are deciding whether this is the record that was written, or a record written since.

If the log can be rewritten
Every other guarantee in the system is a claim about the past with nothing holding it up, including the approval this removal rests on.
If it cannot
You know what was recorded and that nobody has touched it since. You still do not know that what was recorded was true.

Tamper-evidence is a narrow promise, and the narrowness is the point: it turns a claim about the past into an artifact somebody can check.

chain verified · 5/5 links

head matches the externally anchored head

untouched · this is the log as it was written

head32432f72b245d8850e22644111c1de64c4907f50e697cc5613490c7e7827ddbf
anchor32432f72b245d8850e22644111c1de64c4907f50e697cc5613490c7e7827ddbf
head == anchor

the anchor is a copy of the head kept where this system cannot reach it · notary:transparency-log@2 · witnessed 14:02:07Z

Try this, in order

  1. Rewrite an entry — type into it, or take the forgery someone would actually write.Verification stops at the index you touched, and names it. — do this next
  2. Recompute that entry's own hash so it commits to what it now says.The break does not go away. It moves to the next entry, which committed to the hash you just replaced. — not yet
  3. Carry the repair to the end of the log.Every link holds again — and the head stops matching the value witnessed outside the log. — not yet
Entry to rewrite

Every entry in a review log is worth something to somebody. What rewriting #02 would buy: Written by someone arguing the removal was unjustified: with this channel at 0.06 the whole case rests on one model.

one forgery at a time · choosing a different entry restores the log first, so the break has exactly one place it could have come from

  1. #00ingest2026-07-24T14:01:52Zverified
    pipeline:ingest@1.4.2

    asset=ast_9f2c41 surface=comments reports=3

    reads · The comment arrived from the comments surface with three user reports against it.

    prevprev 0000000000000000000000000000000000000000000000000000000000000000hashhash bfc7a4c8a107d6c3cec96d72693b4b0627d4da1f69248d02328465d33cc215a3
  2. #01.prev == #00.hash
    #01signal2026-07-24T14:01:54Zverified
    skill:safety-classifier@2.3.0

    channel=harassment.targeted p=0.94 conf=0.88 span=0..92

    reads · The safety classifier put targeted harassment at 0.94 and pointed at the whole comment.

    prevprev bfc7a4c8a107d6c3cec96d72693b4b0627d4da1f69248d02328465d33cc215a3hashhash dae81aa483a955ca6e984cf9251e3910c4f04c25b2c09532117aa528274e2f39
  3. #02.prev == #01.hash
    #02signal2026-07-24T14:01:58Zverified
    skill:threat-heuristics@0.9.4

    unmodified — rewrite any character to forge the record

    reads · The threat heuristic put an implied threat at 0.71, pointing at twenty-three characters in the middle.

    prevprev dae81aa483a955ca6e984cf9251e3910c4f04c25b2c09532117aa528274e2f39hashhash a472056f88180cea8070258701f2b200923940c07a7a933f72e326646b3afff2
  4. #03.prev == #02.hash
    #03routed2026-07-24T14:02:01Zverified
    policy:review-router@4.1

    rule=R-07 gate=mandated action=remove_content

    reads · The router matched rule R-07 and held the removal for a human.

    prevprev a472056f88180cea8070258701f2b200923940c07a7a933f72e326646b3afff2hashhash 47013406396cc68fae71517d202bc4140e4273e235494d2d6fe8413785358c0a
  5. #04.prev == #03.hash
    #04decided2026-07-24T14:02:06Zverified
    human:reviewer-2

    decision=approved action=remove_content latency_s=41

    reads · Reviewer 2 approved the removal, forty-one seconds after being asked.

    prevprev 47013406396cc68fae71517d202bc4140e4273e235494d2d6fe8413785358c0ahashhash 32432f72b245d8850e22644111c1de64c4907f50e697cc5613490c7e7827ddbf

Controls

Recomputing makes the broken entry commit to its own new content. It fixes that index and breaks the next one, because the next entry already committed to the hash the forged entry used to have. Keep going and the break walks to the end of the log, where the head stops matching the value witnessed outside it.

digestsha256( index ⎹ ts ⎹ event ⎹ actor ⎹ payload ⎹ prevHash )
anchored2026-07-24T14:02:07Z
computedin your browser, on every keystroke — real SHA-256, not a stand-in

proves · the recorded history has not been altered since it was written

does not prove · that what was written was true at the time, that the reviewer read the evidence before deciding, or that the policy was reasonable. It turns a claim about the past into an artifact. It does not turn a judgment into a fact.

What it does not prove deserves equal space. The chain shows that the recorded history has not been altered since it was written. It says nothing about whether what was written was true at the time, nothing about whether the reviewer read the evidence before clicking, and nothing about whether the policy was reasonable. It converts a claim about the past into an artifact. It does not convert a judgment into a fact.

6. What the build proved about the family

This project was intended as the proof that the akaOSS kits compose into a real application. It proved something less flattering first.

HITL Kit is genuinely wired in: UI primitives installed from the live registry, and the gates package imported as the decision state machine described above. Remove the dependency and the application stops working, which is the only definition of "uses" worth accepting.

The other two are not. tag-kit was never published to npm. eval-kit's gate release exists on main and has not shipped. So the scope-aware tagging layer and the calibration surface in this build are local implementations standing exactly where those packages will plug in, and they are labelled that way in the repository rather than described as integrations.

This matters beyond bookkeeping. The credibility stack argued that a package which works inside its own monorepo can be broken for every outside consumer, and that the fix is to test the artifact a stranger actually receives. This is the same failure one level up: a family of kits can look coherent from the inside and be un-composable from the outside, and the thing that reveals it is trying to build the first honest consumer. The previous version of this project claimed both integrations in its README while importing neither. That is the specific mistake this note exists to not repeat, and the reason the limitation is in the summary rather than a footnote.

7. What is real and what is demonstration

Real: the contracts, the policy evaluator (a structured AST, first match wins, no string evaluation anywhere, and the matched subtree is preserved so an audit entry can say precisely why), the hash chain and its verification, the calibration math, the gate state machine, and the review surfaces.

Demonstration: the corpus is twenty hand-authored events with pre-computed signals. No model is called. There is no database, and decisions reset when the process restarts. The gold set is thirty rows, which is enough to show that the Brier and calibration-error arithmetic is plumbed correctly and nowhere near enough to claim anything about how well any classifier is calibrated in the world.

The connectors that would carry real platform events do not exist, which is the honest ceiling on the whole thing: it is a verification substrate with a reference interface on top, not a moderation service. That was true of the prototype this replaces, and stating it clearly was the best thing about that project's documentation. It seemed worth keeping.

Keywordsapproval gates, human oversight, hash-chained audit, evidence pointers, calibration, reference architecture