Or: what happens when every tool on the market assumes a fact about your team that isn't true
TL;DR — the code and the full prompt library live at https://github.com/architect4hire/plannerpro. The prompts document at docs/PLANNERPRO-SCRUB-PROMPTS.md is the interesting part. This post explains why.
I want to tell you about a standup.
Not a dramatic one. Nobody quit. Nothing shipped broken. The kind of standup that's much worse than that — the kind that happens every single morning, takes forty minutes, and produces less clarity than it started with.
The client had one delivery team. Six people. Good people. And at the moment I was engaged, that one team was carrying three completely independent projects: a customer portal rebuild, an internal reporting migration, and a compliance-driven integration with a hard regulatory date. Three backlogs. Three stakeholders. Three sets of priorities that had never once been in the same room together.
Their tooling handled this the way every project management tool handles it: three boards. Because every tool on the market is built on the same silent assumption — that a project has a team. One project, one board, one team, tidy.
This team had the assumption backward. The team had projects. Three of them. And the tooling had no answer for that at all.
So, the standup was tab-switching. Board one, board two, board three, back to board one because someone remembered something. The team lead kept a spreadsheet on the side — the real board, the one that actually reflected reality — updated by hand, wrong by Wednesday. And the thing that finally made someone call me: their most senior developer had nine items in progress. Nine. Three per board, and each board looked completely reasonable in isolation. No single view existed anywhere in the company that could show that number. The person carrying the most risk in the org was invisible to every tool they'd paid for, precisely because the risk was spread across three views that never met.
That's not a process problem. That's a data model problem. The unit of capacity is the person, and every board they owned modeled capacity as a property of the project.
I couldn't find a tool that modeled it correctly without an enterprise license and a consulting engagement to configure it. So I built one. It's called PlannerPro; it's small, and it does exactly one thing the big tools don't: one board, columns for workflow state, swimlanes for projects, and work-in-progress limits that belong to the human being — enforced across every project simultaneously.
This post is about what I built, but mostly it's about how I built it — because PlannerPro went from an empty repository to a working, tested product using a committed library of small, layered prompts, and the discipline behind those prompts is the part you can steal.
SCRUB, For Those Just Joining Us
If you read the BrandForge post, you know the framework. Short version for everyone else.
AI coding tools have a failure mode I call the Plausibility Trap: the output isn't wrong in ways that crash. It's wrong in ways that compile, pass tests, look idiomatic, and quietly encode a domain decision you never made. The model knows how to write code. It does not know your domain. When your prompt leaves a gap, the model fills it with the most statistically common answer from training data — and the most common answer is, occasionally and silently, the wrong one for you.
SCRUB is the checklist that closes those gaps before you hit submit. Five elements, every prompt:
- S — Scope. Exactly what you're asking for. Named methods, named files, nothing else.
- C — Constraints. The technical rules: framework versions, architecture, naming, DI style. Your codebase, not the training-data average.
- R — Restrictions. What you explicitly do not want, tiered CRITICAL / IMPORTANT / PREFERRED, each with a reason. The element everyone skips. The element that matters most.
- U — Usage. Who runs this, under what conditions, with what compliance and audit expectations?
- B — Behavior. What must not change. The star of every edit to existing code.
I've written a book about the full system — Pratical AI Assisted Development, out soon — which goes deep on tiered restrictions, Edit Mode patterns, prompt chaining, and applying the framework to codebases you inherited rather than started. But you don't need the book to follow this post. You need one idea from it:
A prompt is a specification, and specifications deserve engineering.
PlannerPro is what that looks like on a greenfield project, end to end.
The Architecture and the Choices Behind It
Let me get the stack out of the way, then talk about the decisions that actually mattered.
.NET 10 Web API on the backend. EF Core 10 over SQL Server. Angular 20 on the front, standalone components, signals for all component state, Angular CDK for the drag-and-drop. If you've read anything else I've written, none of that surprises you.
Here's the first real decision: PlannerPro is one API project. Not microservices. Not Aspire orchestration. One Web API, one database, one Angular app.
I want to dwell on that for a second, because I've spent a lot of this blog building distributed systems, and I can feel some of you raising an eyebrow. The honest architect's answer is that this system has one team of users, one write path that matters, and no independent scaling or deployment pressures anywhere. Microservices are a solution to organizational and operational problems that PlannerPro lacks. Reaching for them anyway would have been resume-driven development, and the whole point of this engagement was to solve a problem in days, not to build a platform. Right-sizing is an architectural decision too — arguably the one clients pay me for most often.
Inside that one project, the layering is strict, and it's the same layering I use everywhere: Controller → Facade → BusinessManager → DataManager → DbContext. Hand-rolled mapping extensions, no AutoMapper. Audit fields on every entity. Soft delete everywhere, enforced with a global query filter. Boring, deliberate, and — this matters for what comes later — predictable enough that an AI can be pointed at it and told to conform.
The domain model consists of three entities. Project. WorkItem. TeamMember. That's it — no sprints, no epics, no comments, no attachments. Version one is the board and the rule, and everything I declined to build was declined on purpose.
And the rule is the whole product, so let me state it precisely:
A team member's WIP limit applies to their in-progress items across all projects. The query that backs it — CountInProgressForMemberAsync — deliberately has no project filter. There is a CRITICAL restriction in the prompt library that says exactly that: do not filter this count by project. Because an AI generating a data manager for a project-management tool will absolutely scope that query to the project. Every example in its training data does. Project-scoped WIP is the idiom. Project-scoped WIP is also, for this client, the entire disease. Their senior dev with nine in-progress items looked fine on every board precisely because every count was project-scoped. One .Where() clause, present or absent, is the difference between a tool that solves the problem and a tool that re-implements it with nicer CSS.
That's the Plausibility Trap in one sentence, by the way. The wrong version compiles, passes tests, and demos beautifully.
Two more decisions worth naming:
The board is one payload. A single GET /api/board returns everything the screen needs, grouped and ready to render — the Angular component does no client-side regrouping. When five people are staring at a shared screen in standup, you want one fetch, one render, one source of truth.
The server is the transition authority. Status moves one step forward or one step back — Backlog → Ready → In Progress → Review → Done, no skipping — and the server enforces it. The UI does a drag with an optimistic update and rolls back on rejection. I explicitly restricted the AI from greying out "illegal" columns during a drag, which is the clever UX idea it will offer you unprompted. A greyed-out column that's wrong is a lie with good intentions; an honest 422 toast after the fact is merely an inconvenience. When client-side cleverness and server truth disagree, cleverness loses.
One drag-and-drop restriction earns special mention because it's my favorite in the whole library: drop lists are connected only within a swimlane. A card can move between columns, never between projects. Why CRITICAL and not IMPORTANT? Because cross-lane drag isn't a UX bug — it's a data corruption bug wearing a UX feature's clothes. Dragging a card one lane down would silently reassign a work item to a different project, and the CDK will happily wire all the lists together if you don't tell it otherwise, because connecting everything is what the examples do. One restriction. One line. An entire category of "why is this ticket in the wrong project" support conversations, never born.
The Prompts: Small on Purpose
Now the part I actually want to talk about.
There's a file in the repository — docs/PLANNERPRO-SCRUB-PROMPTS.md — that existed before any of the code did. Nineteen prompts. It's the plan I worked from, committed alongside the code it produced, and it follows the same chain the BrandForge component did: Context → Plan → Execute → Verify.
But the style of these prompts is different from what most people write, and the difference is the point. These are micro prompts. Every one of them fits in a paragraph. The Scope blocks name exact methods with exact signatures. The Restrictions come tiered, and every CRITICAL restriction carries its reason — not as documentation garnish, but because the reason is the thing that tells the model why the plausible default is wrong here.
Compare the mega-prompt approach — "build me a kanban tool with three projects and WIP limits, here are 40 requirements" — with what's in that file. The mega-prompt produces two thousand lines in one shot. Somewhere in those two thousand lines, the WIP query has a project filter, the drop lists are cross-connected, and the rollback path doesn't exist. You will find those problems the way my client found their nine-item developer: later, in production, by accident.
The micro prompts produce the same two thousand lines. But no single generation is ever more than a couple hundred of them, and every one of those chunks got a review that took two minutes instead of an afternoon.
Each prompt also names its mode, because the tools are not interchangeable. Claude Code Chat for the layers where thinking matters — contracts, business rules, orchestration methods. Copilot Agent Mode for the layers that are mechanical and multi-file — data access, template slices, CSS — where the autonomous build-and-verify loop earns its keep. Claude Code Edit Mode for surgery after the fact, with Behavior as the dominant element. Reasoning in Chat, generation in Agent, precision in Edit. Matching the mode to the cognitive work is half the discipline.
Why Layers Are the Whole Ballgame
If you take one thing from this post, take this section.
The single most important structural decision in the prompt library is not any individual restriction. It's that the work is layered, and each layer constrains the next.
The backend is four layers. Layer 1 is contracts — interfaces, enums, entity skeletons, view model shapes. Signatures only, no bodies, maybe eighty lines. Layer 2 is data and mapping: the DbContext, the data managers, the mapping extensions. Layer 3 is the business rules — the transition table and the WIP check, the two things that are the product. Layer 4 is the HTTP surface and the wiring.
The frontend gets the same treatment, six layers deep: a signal skeleton with every method stubbed, then the template built in vertical slices — header and WIP strip, then the swimlane grid, then cards and drag wiring — then the method bodies, then the styles. The methods layer comes second to last deliberately: by the time the AI writes onDrop(), the entire template it orchestrates already exists in front of it. It is not writing orchestration in a vacuum and hoping the UI that arrives later happens to match.
Why does layering matter this much? Four reasons, in ascending order of importance.
First, review becomes possible. Eighty lines of contracts is a code review. Two thousand lines of everything is a skim, and a skim is where the Plausibility Trap lives. Every ordering bug I've ever shipped from AI-generated code went out through a review I did too fast because the diff was too big.
Second, errors stay local. When Layer 2 has a mapping bug — and mapping bugs are the most common failure at that layer — I fix it with a Layer 2 prompt. Layers 3 and 4 never hear about it, because they reference Layer 2 through its contracts, not its internals. In a one-shot generation, a bug in one region sprays corrections across the entire output, and every correction risks disturbing something that was right.
Third, naming compounds. Layer 1 is where I spend disproportionate review time, because the names chosen there propagate through every subsequent layer. A bad facade method name in Layer 1 costs five minutes to fix in Layer 1 and a five-file threading exercise to fix after Layer 4. Cheap-to-fix and expensive-to-fix are the same mistake at different timestamps; layering lets you always pay the cheap price.
Fourth — and this is the one that changes how you work — each layer constrains the next. By Layer 3, the AI isn't inventing; it's conforming. The contracts exist. The data managers exist. The custom-instruction files pre-load the conventions. The generation space has been narrowed, layer by layer, until the plausible output and the correct output are nearly the same thing. That's the real trick. You're not hoping the model guesses right. You're removing its opportunities to guess.
The ordering echoes onion architecture, but the mapping isn't the point — the size is the point, and the direction of dependency is the point. Small enough to read in two minutes. Ordered so the shape exists before the behavior, and the behavior exists before the wiring.
And it doesn't feel slow. I expected it to, the first time. Nineteen prompts for a two-project solution? It felt fast, because I was never working on more than one small piece at a time, and I never once entered the mode every developer knows too well — spelunking through a giant generated diff looking for the line that's lying to you.
Fix the plan, not the code. If the plan is wrong, you fix a paragraph.
Verify: The Step That Catches You Moving Too Fast
The chain ends with Verify prompts — one for the backend, one for the board component. Each is a list of specific PASS/FAIL checks the AI runs against the finished code, with file-and-line evidence required for any FAIL.
Not "does it work." Specific. Does the WIP count query have a project filter? Is any drop list connected across lanes? Does the rollback restore captured state, or does it cheat and re-fetch the whole board? Does any orchestration method contain a .subscribe()? Every check on those lists maps back to a CRITICAL or IMPORTANT restriction from an earlier layer, because the Verify step exists for exactly one scenario: the day you review a diff too fast. On BrandForge, Verify caught a real bug my eyes had slid over. It stays in the chain forever.
The Verify lists are also the maintenance contract. When PlannerPro grows — per-column WIP limits are the obvious next ask, and a "blocked" flag is right behind it — the prompts document gets updated first: new restrictions, new Verify checks, then run the prompts. The document is versioned, committed, and reviewed like the code, because it is the reason the code looks the way it does. Six months from now, when somebody wonders why CountInProgressForMemberAsync pointedly ignores the project, the answer isn't in a comment or in my head. It's in a file with a commit history.
What the Client Got
A board their standup actually runs on. Swimlanes for the three projects, five columns, drag-and-drop with honest server-side rules, and a WIP strip across the top showing every team member's cross-project count — the number that used to live in a hand-maintained spreadsheet, wrong by Wednesday.
The nine-item developer, by the way, is now capped at three, and the standup conversation changed shape the first week: it stopped being three sequential status reports and started being an argument about which project gets the constrained person next. That's the right argument. The tool didn't resolve their priority conflict — no tool does — but it made the conflict visible on one screen, which is the precondition for resolving it like adults.
And I got a repository where every line of code traces back to a small, reviewed, committed prompt — which means the next feature starts from a plan, not from archaeology.
The Practical Version
If you want to run this pattern on your next greenfield build:
Write the domain brief first, and make the AI recite it back. One paragraph. The Context step costs five minutes and catches misunderstandings while they cost one sentence to fix.
Argue with the Plan. In prose, before any code exists. The plan step is the cheapest insurance in the entire chain, and it is the step everyone skips.
Layer the Execute. Contracts, then data, then rules, then wiring. Four to seven layers, each describable in three sentences. If a layer needs a fourth sentence, split it.
Write Restrictions before Scope, and tier them. Ask what could compile and still be wrong. CRITICAL means silent wrong data. IMPORTANT means debt. PREFERRED means style. Attach reasons to the CRITICAL ones — the reason is what teaches the model why the idiom is wrong here.
Find your no-project-filter. Every domain has one: the single line where the training-data idiom and your business reality point in opposite directions. Yours might be a tip default, a rounding rule, a scoped query. Find it before the AI does, and pin it with a CRITICAL restriction.
Match the mode to the work. Chat for contracts, rules, and orchestration. Agent for mechanical multi-file layers. Edit for surgery, with Behavior dominant.
End with Verify, and keep it. Specific PASS/FAIL checks derived from your restrictions. Re-run it after every change.
Commit the prompts. They're not notes. They're the architecture record.
The repo is at github.com/architect4hire/plannerpro, prompts and all. Read the prompts document before the code — it's the better half of the repository, and it will make the code make sense in a way the code alone can't.
Robert Felkins is the principal architect at Architect4Hire and the author of the upcoming book Pratical AI Assisted Development, which covers the full SCRUB system — tiered restrictions, layered generation, Edit Mode patterns, prompt chaining, and applying the framework to codebases you inherited rather than started.
And if your team is staring at a version of the three-boards problem — tooling, architecture, or an AI-assisted delivery practice that isn't paying for itself yet — that's the work I do. I take on fractional architecture engagements: a few days a month of senior architectural judgment without the full-time price tag, covering .NET and Azure platform work, team enablement, and AI-assisted development practices that survive contact with production. Reach out at robert@architect4hire.com.





