Track 4 · Autopilot Agent · Qwen Cloud

The night audit finally sleeps.

An autopilot night auditor for small hotels. Innkeeper reconciles three ledgers, adjudicates every mismatch with cited evidence and competing hypotheses, auto-clears only inside an expected-loss policy gate — then signs the night's books cryptographically.

Runs offline, zero API keys  ·  innkeeper run --night 2026-07-04

0/281
Mismatches auto-cleared
0
False clears · 30 nights
0
Tests passing
0%
Line coverage

Seeded 30-night month · 1,199 transactions · deterministic offline benchmark — regenerate with python scripts/bench.py (docs/BENCH.md)

One devastating query

Watch a night close itself

Real output from the seeded night of July 4th — fetch → extract → match → adjudicate → gate → signed close.

Innkeeper hero panel — the night audit pipeline from three ledgers to a signed close

The $310 card capture with no folio is the planted true error — it can never auto-clear, at any confidence.

The machinery

Math over typed fields, not vibes

Every verdict is a typed object: classification, ≥2-system evidence citations bound by sha256, competing hypotheses, and a confidence the gate can compute over.

The expected-loss policy gate

Auto-clear is a decision-theory inequality, not a button. Two hard constraints can only ever queue: a true_error classification, and a two-pass extraction disagreement.

auto_clear confidence ≥ 0.85 materiality ≤ $50 class ≠ true_error
generalised: E[loss] = amount × (1 − confidence) τ

The published τ-sweep shows the false-clear column stays 0 at every threshold — the risk knob trades automation against review load without ever touching the safety floor.

Two-pass VL extraction

The OTA statement is a real rendered PDF. qwen3-vl-plus reads it twice (t=0.0 / 0.4) with bounding-box citations — agreement becomes confidence, disagreement escalates. Never silently averaged.

3 in-repo MCP servers

PMS folios, processor settlements, and the OTA statement are fetched through three MCP-compatible stdio servers — typed, logged tool calls that form the replayable decision log.

Signed Merkle night-closes

Each night's verdicts form a Merkle tree; the root is Ed25519-signed and chained to the previous night. Flip one byte anywhere and innkeeper verify-chain fails — by test, not by promise.

Byte-identical replay

innkeeper replay --night N re-derives every verdict, the Merkle root, and the signature from stored evidence — byte for byte, with zero API keys. Auditable forever, invariant I4, tested across all 30 nights.

Honest by design

What's real, what's mocked — in writing

The README leads with a MOCK-SYSTEMS banner. These are the same disclosures, because a project about signed evidence should not fudge its own.

Disclosed mockThe three source systems

PMS, card processor, and OTA are mocks shipped in this repo as three MCP servers reading a committed, deterministically seeded 14-room month — with planted, ground-truth-labeled discrepancy archetypes and real reportlab-rendered OTA PDFs. No live hotel system is contacted.

Default transportOffline runs use FakeQwen

The demo and all 404 tests run on FakeQwen — a deterministic stand-in that computes verdicts from each mismatch's own arithmetic and memos, and never reads the ground-truth labels. The graded core needs zero API keys.

Key-gated real path--live calls the actual models

With a DASHSCOPE_API_KEY, the same pipeline rasterizes the committed 8-pt PDF for a real qwen3-vl-plus two-pass read and adjudicates the residue with qwen3.7-max + thinking. Citations bind to on-disk sha256 hashes either way.

ScopeDecision support, not books of record

Innkeeper produces signed evidence and a morning queue — it is not an accounting system of record. The 02:00 Function Compute timer is configured in infra/fc/ but not deployed; runs shown here are invoked locally.

Front desk

Questions, answered

What exactly is mocked, and what is real?
The three source systems (PMS, processor, OTA) are in-repo mocks: MCP servers over a seeded, ground-truth-labeled month with real reportlab-rendered PDFs. Everything downstream is real — the matcher, the adjudication schema, the expected-loss gate, the Ed25519-signed Merkle chain, the replay, and the 404-test suite. The --live flag swaps the deterministic FakeQwen transport for real qwen3-vl-plus / qwen3.7-max calls.
Does it really run offline with zero keys?
Yes. innkeeper seed, run, replay, verify-chain, bench, and the full pytest suite need no network and no API keys — scripts/verify_offline.py even socket-guards the proof. Only --live touches the network, behind DASHSCOPE_API_KEY.
How does the gate math actually work?
A mismatch auto-clears only when confidence ≥ 0.85 ∧ materiality ≤ $50 ∧ class ≠ true_error — generalised as E[loss] = amount × (1 − confidence) ≤ τ. Two constraints are absolute: a true_error classification and a two-pass extraction disagreement always queue, no matter the confidence. That's why the $310 walk-in at 0.60 confidence lands in the morning queue and the $5.67 commission at 0.94 clears itself.
How do I run it?
python3.12 -m venv .venv && source .venv/bin/activate, then pip install -e ".[dev]", innkeeper seed --nights 30, and innkeeper run --night 2026-07-04. The full copy-paste script — including the tamper-detection and benchmark beats — is in DEMO.md.
What happens to the transactions it won't clear?
They arrive in the owner's morning report (innkeeper report --night N) carrying competing hypotheses with probabilities — e.g. "room sold at the terminal, never posted" at 0.60 vs "mis-keyed deposit" at 0.40 — plus evidence citations from at least two systems, each bound by sha256. The agent tells you what it doesn't know.
What's the license?
MIT — the whole repo, including the three seedable MCP mock servers, which are reusable for anyone building reconciliation agents. © 2026 Edy Cu.

Last call

Close tonight's books
before you even wake up.

Clone it, seed a month, and watch 39 transactions reconcile, gate, and sign themselves — offline, in under a second.

git clone https://github.com/edycutjong/innkeeper && cd innkeeper