Skip to content

Guardrailed operations agent

NORA

An AI operations agent that can move money — and the deterministic guardrails that stop it from doing something expensive and stupid.

PrototypeAIFull Stack

Runs locally against generated mock data for a fictional company. Nothing touches a real system; email tools write drafts that are never sent.

Source is private
Period
Aug – Sep 2026
Status
Prototype
Role
Agent design, Guardrail and policy engine
Context
Repository name: ops-copilot. "Ops Copilot" and "Nora" are the same project.
NORA’s landing screen: a glowing green wireframe orb above the NORA wordmark and a prompt field reading "Ask her something, or tell her to act on it".

Screenshot of the running product.

The problem

Why this needed to exist

A model can answer support questions — that part is solved. The question this prototype tests is harder: what stops an agent that is allowed to move money from doing something expensive because a customer message told it to?

A rule written in a system prompt is a suggestion the model can be argued out of. An operator needs limits that hold regardless of what the model is persuaded of.

Who it’s for

  • Operators of small direct-to-consumer brands
  • Support and fulfilment leads
  • Anyone evaluating agents with real-world write access

The solution

How it works

NORA has situational awareness (whole-book sweeps rather than single lookups), real tools against a mock order database, company policy it searches before acting, and a deterministic guardrail layer between every write tool and its effect.

A blocked action returns to the model as an ordinary tool result, so it reads the reason and adapts — usually by escalating to a human — instead of retrying blindly.

Where the guardrail sits

  1. Operator
    • Web console
    • CLI
    • Approvals
  2. Agent
    • Claude + Tool Runner
    • Frozen system prompt
    • Session context
  3. Policy
    • Risk tiers
    • Refund rules
    • Channel limits
  4. Tools
    • Intel sweeps
    • Orders & refunds
    • Social / email / calendar
  5. Record
    • Mock store (SQLite)
    • JSONL audit log
    • Evals
Every write passes the policy engine before it reaches the store. Every call — allowed, held or denied — lands in the audit log.

Implemented

What’s actually built

  • Risk-tiered tools

    Every tool is READ, WRITE_LOW or WRITE_HIGH. Reads are free; refunds, customer emails, social publishing and campaign sends pass through the policy engine.

  • A refund policy in code

    Over $200 is refused outright. Over $50, older than 30 days, or a customer refunded in the last 90 days is held for approval. Everything else is allowed.

  • Fails closed

    If no approver is wired up, held actions are denied. In the web console, no answer within three minutes, a closed tab or a dropped session all resolve to denied.

  • Approval cannot override refusal

    Operator approval is consulted only for actions the engine holds. There is no path from a pasted customer message to a $5,000 refund.

  • Drafting is free; reaching an audience is not

    A social post or email campaign can be drafted immediately. Scheduling it to real followers is held for sign-off, and each email provider’s real daily send limit becomes the ceiling.

  • The operator connects accounts, never the agent

    NORA has no tool to connect a social or email account. She can see which are live, and is refused outright if she tries to post to one that is not.

  • Morning brief

    The CLI opens by scanning the book and telling you what needs a decision before you have asked.

  • An eleven-page console

    Dashboard, Approvals, Audit Log, Social, Email, Calendar, Orders, Customers, Shipments, Policies and Evals — with NORA as a slide-over on every page and a Ctrl/⌘K command palette.

Interface

Screens from the real product

  • Approvals: the guardrail rules and safety properties, visible rather than taken on trust.
  • The dashboard reads the local store directly — no API key needed.
  • Every tool call is appended to a JSONL audit log; the evals score this log, not the model’s prose.
  • Scored scenarios: happy path, must-escalate, prompt injection, out-of-scope and more.
  • Policies the agent searches before it acts.

My role

What I did

Everything in the prototype: the agent wiring, the tool surface, the guardrail engine, the mock-data generator and policies, the evaluation suite, and the web console — FastAPI and Jinja2 with hand-rolled SVG dials and charts, no build step and no chart library.

Responsibilities

  • Agent design
  • Guardrail and policy engine
  • Evaluation harness
  • Web console (UI + backend)

Stack (verified from source)

  • Python
  • Claude (Anthropic Tool Runner)
  • FastAPI
  • Jinja2
  • Vanilla JS
  • Hand-rolled SVG
  • SQLite
  • JSONL audit log

Decisions

Technical and design choices

  1. 01

    Guardrails in Python, not the prompt

    The prompt describes the policy so the model can plan around it; the if-statements enforce it. The model never gets to decide whether a rule applies.

  2. 02

    Score what it did, not what it said

    Evals read the audit log. An agent that says "I’ve escalated this" without calling the escalation tool fails the case.

  3. 03

    A frozen, cacheable system prompt

    Volatile facts like today’s date arrive as a separate system message, keeping the cached prefix intact. A run with zero cache reads flags that something dynamic leaked into the prefix.

  4. 04

    One seam for real systems

    All data access goes through one module, so SQLite can later become Shopify or Stripe without touching the agent.

Trade-offs

Challenges along the way

  1. 01

    Prompt injection through customer text

    A dedicated eval bucket feeds the agent customer messages that try to talk it into refunds. The guardrail, not the model’s judgment, is what makes those cases pass.

  2. 02

    Approval UIs that fail open

    Browser approval loops have many quiet failure modes — timeouts, two tabs, shutdowns, double-resolves. Each is covered by an offline check that must resolve to denied.

  3. 03

    Undeclared dependencies

    The console worked locally only because packages had been installed by hand; a clean install could not start the server. The fix was declaring them — and the habit of installing your own project somewhere fresh.

Results

What can be checked

  • Evaluation suite

    33 scored scenarios across seven buckets (happy path, auto-approve, must-escalate, injection, out-of-scope, briefing, channels), plus 27 offline guardrail assertions that run in about a second.

Lessons

What I took away

  • The hard part of a production agent is the integration surface and its limits, not the conversation.
  • Making the guardrail observable — an Approvals page, an Audit Log — turns safety from a claim into something an operator can watch.

What’s next

Planned — not built

Planned work

  • Real integrations (store platform, payments, email) behind the existing data seam
  • Authentication and multi-tenancy
  • Deployment beyond a single local operator

Current limits, stated plainly

  • Mock data for a fictional company; no real customers or orders.
  • Sends and publishes are simulated even where a draft is produced.