Fun With Agentic AI

Fun With Agentic AI

“Agentic AI” gets used to describe everything from a single chatbot with a plugin to a fully autonomous back office. This demo is representative of applications that I create at the day job: a permit review workflow modeled on how a city building department actually processes a construction permit, end to end.   Don’t get me wrong – I make them for home life as well (a future post will show my fantasy football agents :-).

This post walks through what I built, what it checks, and the one lesson that mattered most: the more consequential the decision, the less I wanted a language model anywhere near making it.

What This Is (and Isn’t)

To be upfront about it: this isn’t connected to any real city’s permitting system, and the permits in it aren’t real applications. The plan sets are generated documents, the applicants are invented, and the addresses were picked for plausibility, not accuracy.

What is real is the process shape. I researched how an actual city’s building and permits division is structured — which departments review what, how an outside agency like a county health department fits in, and where a permit can get stuck — before writing a line of code. Then I deliberately narrowed the scope to one permit type and one external agency, so the system stays legible instead of sprawling to cover every case a real department handles.

The First Job: Reading the Document, the Same Way Every Time

Every submittal in this system arrives as a plan set — a multi-sheet PDF: a cover sheet, then architectural, structural, foundation, life-safety, electrical, mechanical, and plumbing sheets. Before anything gets decided, the system opens that document and reads it, sheet by sheet, against the same checklist a plans examiner would use for an intake completeness review:

  • Is every sheet sealed and signed? Each page is checked for a licensed design professional’s seal. A contractor’s license number quoted on the cover doesn’t count — only a design professional’s actual seal does.
  • Is anything stamped “preliminary” or “not for construction”? A sheet carrying that language isn’t admissible for review, no matter how complete it otherwise looks.
  • Does the cover sheet name a designer of record and include a sheet index? Both are required, and both are checked specifically on the cover — a sheet index buried on page six doesn’t satisfy the requirement.
  • Is the required code analysis present? Occupancy group, construction type, allowable area, exit analysis, and sprinkler requirement all have to appear.
  • Does every sheet actually belong to this project? Each sheet is checked against the project’s own address, so a plan set for a different property can’t satisfy the requirement by carrying some address of its own.
  • Are structural calculations present, and if so, are they sealed? These two facts are tracked separately on purpose — a missing calculation sheet and an unsealed one are different problems, and collapsing them would misreport which one actually occurred.

None of this is a model reading the document and forming an impression. It’s a direct extraction of the PDF’s own text layer, checked against explicit criteria — the same finding comes back every time for the same document, which is exactly what an intake check has to guarantee. A submittal that fails shows up as a specific, itemized list, not a vague rejection:

Four separate, actionable findings — not “your submission was incomplete.”

The Path a Permit Actually Takes

Once intake is checked, the permit moves into department review, and this is where the workflow stops looking like a checklist and starts looking like an actual process:

  • Departments review in parallel, not in sequence. A building department and an outside health department (for occupancies like group homes or day care) both watch the same permit independently. Neither waits for the other, and neither is assumed to go first.
  • Each department’s status is one of four things: approved, rejected, actionable (a reviewer can decide right now, nothing is blocking them), or unconfirmed — reserved specifically for an external agency where this system has no visibility into their process at all, and says so honestly rather than pretending to track a queue it can’t see.
  • The overall outcome is computed, not asserted. It’s a function of every department’s status and every intake finding together: ready for issuance only when every required department has approved and no findings remain open; corrections required if anything failed; in review otherwise.
  • Corrections don’t reset the whole permit. If one department rejects a submittal, only that department’s approval resets when the applicant resubmits. A department that already signed off isn’t asked to review the same site plan twice.
  • Issuance re-checks everything at the moment it’s requested. A permit isn’t issued because a review said so an hour, or a week, ago — issuing re-runs the entire check fresh, right then, and only proceeds if that current result is actually ready. Nothing can be issued just by asking for it.

A reviewer can see exactly where a given permit sits in that path at a glance: which department is holding things up, whether it’s stuck on an outside agency’s response, or whether it’s genuinely ready to move.

Status doesn’t require clicking through a UI to find, either. A plain-language prompt — “What’s blocking BLD-2026-0143?” — routes to the same underlying facts as the visual view and answers directly, so checking on a permit is as fast as asking about it. When a department’s status changes, that same fact reaches a reviewer wherever they’re already working — in this case, a message in Slack (I could have used Teams as well) with the specific findings attached and the decision one click away:

Where the Model Actually Fits

Here’s the part that surprised people when I described this project: out of the entire workflow, exactly one step involves a language model at all.

After the document scan and the department checks are complete, a model is handed those already-computed facts and asked to write them up — a short, plain-English summary for whoever’s reading the permit next. It doesn’t see the raw document, and it isn’t asked to judge anything. The facts and the verdict already exist before it’s called. If that step fails outright, the permit still has its correct status and its correct findings. It’s simply missing a paragraph of prose, never a wrong decision.

That’s the opposite of how a lot of “agentic AI” demos are built, where a model is put in charge of the whole loop and asked to reason its way to an outcome. It’s also, I’d argue, the only version of this that a city (or any organization with real compliance stakes) could actually trust in production: every checkable fact is checked the same deterministic way every time, and the one place uncertainty is allowed to live — the model — is the one place where being wrong costs you a poorly worded sentence, not a wrongly issued permit.

The Takeaway

The pattern I’d carry into any future version of this — including a planned port onto Microsoft Copilot Studio, where a conversational front end would sit on top of the same rules engine and API this prototype already has — is the division of labor itself:

  • A document scan that reads the same document the same way every time.
  • A set of department checks that run independently and report honestly when something is genuinely outside their visibility.
  • An outcome that’s computed, not asserted.
  • A model used for exactly one thing: explaining a decision that’s already been made, never making it.

That division of labor is also what makes a platform port tractable. None of the actual logic needs to be rewritten to move to a new front end — it just needs one.

If there’s one thing worth taking from this project into your own agentic engineering work, it’s this: figure out which decisions in your workflow need to be the same every time, build those as rules you can point to, and only then decide where a model earns a seat at the table.

Tags
, ,

Add a comment

*Please complete all fields correctly

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Blogs