Key takeaways
- An advisor you consult is a better search box. An advisor that reads every prompt and interrupts you is a manager, and the manager is the one that changes behavior.
- Silence is the default. An advisor speaks only when a costly decision is live and the remark would change what you do next.
- Vague triggers fire on everything. One of my rules fired 49 times in 33 hours and 55% of those were false.
- Take the “if” away from the model. Code computes the day, the count and the tier, and injects the exact sentence. The model writes the words.
- Every escape hatch gets logged, and every new rule gets audited from your own transcripts.
An AI advisor in Claude Code is a standing rule that tells the model when to speak without being asked, at the end of a reply, named, in about three lines. A deterministic advisor goes one step further: a UserPromptSubmit hook computes the trigger in code and injects the exact instruction, so the model no longer decides whether to fire.
Everyone on YouTube has an “AI board of advisors”. You paste a persona into a chat, ask the CEO what to do, get a lovely answer, close the tab and change nothing.
Mine does not wait to be asked. I run seven advisors inside Claude Code. They read along with every prompt I send, and now and then one of them cuts in at the bottom of a reply to say the thing I did not want to hear. On Monday to Wednesday, if I try to start the wrong kind of work, one of them refuses the prompt. “Hey, I won’t do that.”
I wrote every one of those rules. I got tired of being the only thing enforcing them.
This is what the seven are, the seven lessons from seven weeks of being nagged by my own setup, and a numbered build you can copy: an instructions block, a hook, a state file, tiers, logged escape hatches, a park-for-later valve and a transcript audit. My goal is $50k a month, and the Chief of staff exists to get me there.
A board you consult is a better Google
A board you consult is search with a costume on. It answers when you ask, about what you asked, and you ask about what you already worry about. The expensive mistakes are the ones you did not think to ask about.
A board that monitors you has three properties.
Unprompted. It speaks when it sees something, not when you open a chat.
Continuous. It runs on every turn of every session, so it is there at 11pm when you are about to say yes to something.
Grounded. It quotes my real numbers from a reference file. Generic advice is what you get from a persona with no data behind it.
Why did I want this? I already knew the rules: two hours per client per week, no custom work, calls do not stretch. I kept breaking them anyway. The gap was never knowledge. It was enforcement, and a chat tab I have to remember to open cannot enforce anything.
Meet the seven
Each advisor has a lane, a lens and a tell it watches for.
- Business. Clients, pricing, roster, scope. Lens: is this making money or leaking it. Its tell is free work leaking out of a paid tier, so it makes me name the tier price before I waive anything.
- Engineering. Code, architecture, deploys. Lens: what breaks in six months, and what am I rebuilding that already exists.
- Chief of staff. My time and my yeses. The star of this post, so more on it below.
- Sales. Calls, proposals, anything going to a prospect. It reads from an audit of 478 of my sales calls, which found $43k of free work given away and scope leak that triples after minute 50. It counts the little words that shrink a price.
- Product. The community, so it never turns into 200 small clients with a custom request each.
- Capital. Hires, tools, subscriptions. Every spend is priced in months of runway, never in dollars.
- Accountancy. Lens: what would an auditor ask, and can I answer with a document instead of an explanation. The test is “no document, no claim”.
The Chief of staff carries a filter for inbound asks that you can steal today. Does the ask name a problem, a deadline and who pays? One missing is a maybe. Two missing is an email reply. Three missing is a no. “Let’s pick your brain” scores zero by definition.
It also holds my standardization rule: the budget is 2 hours of me per client per week, and anything past that has to justify itself. When I analysed my client work, everything beyond 2 hours a week fell outside my normal SOPs. Outside the SOPs means custom work, and on custom work my odds of success drop. So the cap does two jobs: it keeps me focused, and it stops me overspending time and resources on one project. Client delivery runs through Nask AI, the ads platform I built, and anything that is not a run of an existing system is a leak. Calls stay 30 minutes. Longer or extra calls are the $9k tier, next to the $2.9k and $6k ones, and the default answer to “can we make it an hour?” is no, with the tier named.
The discipline that stops it being annoying
Seven opinionated advisors would be unbearable by Thursday. Six rules outrank all of them.
- Silent by default. An advisor speaks only when a costly decision is live (money, time, a commitment, something shipping) and the remark would change what I do next. Roughly once every few sessions.
- One advisor per message. Never two. The one with the most at stake wins.
- After the work. The note sits at the end of the reply, about three lines. It never replaces the work and never opens it.
- Named. “Chief of staff note:” at the front, so I can mute one without muting all.
- Tone. Blunt, sarcastic, push-back welcome. These are advisors, not a therapist, and I wrote that into the file in those words.
- Two-tier loading. The short version is always in context. The long reference, with each advisor’s known threads and my real numbers, is read only right before one speaks. That keeps the token bill down.

How it grew, from a prompt to a gate
The panel went in on 2 September as six “coaches”, all prompt-only, after a “pick your brain” call that returned nothing. By 28 September I had renamed them advisors and added Accountancy as the seventh. I also wrote the standardization mandate that day: 2 hours per client per week, custom work always flagged.
Then came the miss. On 3 October I spent a day on a small refund dispute on Airbnb, a few hundred dollars. Claude Code drafted every round of it, cheerfully. No advisor said a word. I told it afterward that I should have been shouted at a hundred times. The rule I wrote: the Chief of staff fires early on low-stakes disputes, and the rule keeps the story attached, so a future session knows the reason and not only the instruction.
Five days later, 8 October, I gave Monday to Wednesday to client delivery and wrote the first version of the hook: warn me when I start non-client work on those days. It exists so that no crazy idea takes over my focus from delivering for clients. The hook counts, and on 12 October the count started to matter: a per-week counter, colored tiers, and a hard gate that blocks the prompt.
The same day I wrote an overcorrection. I told the setup to flag it whenever I was about to do ops work by hand. A day and a half later I scraped my transcripts. It had fired 49 times in 33 hours, and 55% were false, mostly while I was building the system it wanted me to build. I narrowed it the next day.
On 17 October came the accountability gate. Before any work that will not add revenue, the Chief of staff stops and asks me to click “yes, this is the best use of my time”. It is a prompt rule, not code. It shows weeks left, never a revenue number.
And today, 23 October, the hook learned to ask first: “Park this for Thursday, or do it now?”
Seven lessons from seven weeks of being nagged
Each one is a failure, a fix and a rule you can copy.
- Quiet advisors fail as badly as noisy ones. The refund day. Fix: write every miss down as a rule, with the story attached.
- Vague triggers fire on everything. “Doing a task by hand” caught logins, approvals and deploys. Fix: a trigger that needs all of several conditions at once, plus an explicit “never on” list.
- An alert with no alternative is a note. 19 of the 49 firings had no suggested route. Fix: every alert names the route and the system fix, or it does not fire.
- The model decides badly when it is busy being helpful. The refund day again. Fix: take the “if” away from the model, which is the next section.
- Escalate, do not nag. Note, yellow, red, refusal, block. Each step costs more to ignore than the one before.
- Escape hatches must be logged. A way out with no record is a hole. Mine write a reason to a log I read on Fridays.
- Audit it like a product. Scrape the transcripts a day or two after any new rule, mark each firing true or false, and tune.
The audit behind lesson 2 is the one that changed how I write rules.

19 had no route to offer
Making it deterministic
Here is the core split. A prompt-judged advisor reads its rule every turn and decides. It is cheap and flexible, and it drifts, over-fires on vague triggers and under-fires when it is busy helping. A code-judged advisor gets the same input and produces the same output, every time.
Only one of mine is code-judged today: the Chief of staff’s client-day check. The other six still rely on the model noticing. I will say what I plan to do about that below.
What you are building is a hook that runs on every prompt. It checks the weekday and your open task cards, bumps a counter in a state file, picks a tier and injects the exact sentence for that tier. The model writes the words. One judgement stays with the model: whether a message counts as client work. Code decides what happens after that answer.
Step 1: Write the instructions block
Start with the discipline from earlier, in the instructions file Claude Code loads on every session. Keep it short. The long reference lives in another file.
## Advisory Panel
Standing advisors speak UNPROMPTED, under this discipline:
- Silent by default. Speak only when a costly decision is live AND it would change what I do next.
- One advisor per message, at the END, about three lines, after the work is done.
- Name yourself ("Chief of staff note:") so I can mute one.
- Quote real numbers from the reference file. Never generic advice.
- Blunt and sarcastic. Advisor and partner, not a therapist.
- Before speaking, read ~/advisors/reference.md for that advisor's lens and known threads.
1. Business: clients, pricing, scope. Lens: making money or leaking it.
2. Engineering: code, deploys. Lens: what breaks in six months.
3. Chief of staff: my time and my yeses. Lens: one thing, not three.
(add yours)
Step 2: Add a UserPromptSubmit hook that injects computed context
Register the hook in settings.json (the hooks reference has the full schema). It receives the prompt, the working folder and the session id as JSON on stdin, and whatever it prints in the right shape is added to the turn.
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [ { "type": "command", "command": "node ~/.claude/hooks/focus.mjs" } ] }
]
}
}
Inside focus.mjs, read stdin, check the day, count your open cards and print the context. The card list is a JSON file here. Swap it for your task tool’s API.
const input = JSON.parse(readFileSync(0, 'utf8') || '{}')
const day = new Date().toLocaleDateString('en-US', { weekday: 'long' })
const cards = JSON.parse(readFileSync(join(DIR, 'cards.json'), 'utf8'))
const open = cards.filter(c => c.column !== 'done').length
if (!FOCUS_DAYS.includes(day) || open === 0) return // silent on other days
const say = text => process.stdout.write(JSON.stringify({
hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: text } }))
Wrap the whole thing in try { main() } catch { process.exit(0) }. It runs on every prompt of every session, so it must fail open. A broken hook can never break a session.
Step 3: Keep a state file and an append-only log
Numbers come from files, never from the model’s memory. The state file holds this week’s count and rolls over on Monday. The log gets one line per event and is never edited.
function load() {
let s = {}
try { s = JSON.parse(readFileSync(STATE, 'utf8')) } catch {}
const week = weekStart()
return s.week === week ? s : { week, count: 0, lifetime: s.lifetime || 0, overrideDay: null }
}
const log = e => appendFileSync(LOG, JSON.stringify({ ts: new Date().toISOString(), ...e }) + '\n')
A hook cannot know whether a message is client work, so the injected text tells the model to run the same script as a writer: node focus.mjs --log violation "<summary>". The script bumps the count and appends the line. The advisor then quotes a number that came from a file.
Step 4: Pick a tier from the count
Escalate, do not nag. The count picks the tier, and each tier has fixed words.
const n = s.count + 1
const tier = n >= 15 ? 'gate' : n >= 12 ? 'refuse' : n >= 7 ? 'red' : n >= 3 ? 'yellow' : 'note'
const WORDS = {
note: `open with one line: "Focus day: ${open} client cards still open. This is not client work." then do the task.`,
yellow: `open with "Focus day, YELLOW: violation ${n} this week." then do the task, and END with a "Chief of staff note:" quoting ${n} and ${open}.`,
red: `open with "Focus day, RED: violation ${n} this week." then do the task, and END with a "Chief of staff note:".`,
refuse: `reply only "Hey, I won't do that. Violation ${n} this week." and do NOT do the task.`,
}
Fixed templates mean every firing has the same shape, which makes it easy to grep and easy to audit.
Step 5: Add a hard gate and logged escape hatches
At the top tier, the hook blocks the prompt itself, before the model sees it. A gate with no exit gets deleted by an annoyed human within a week, so give it three exits and log all of them: OVERRIDE: <reason> once a day, EMERGENCY:, and finishing a client card, which buys one off-topic message. The third is a credit counter in the state file. I left it out of the snippet.
if (tier === 'gate') {
if (/^EMERGENCY:/i.test(prompt)) { log({ kind: 'emergency', summary: prompt.slice(0, 120) }); return say('Emergency, logged. Do the task.') }
const ov = prompt.match(/^OVERRIDE:\s*(.+)/i)
if (ov && s.overrideDay !== today()) {
s.overrideDay = today(); s.count++; save(s)
log({ kind: 'override', reason: ov[1].trim() })
return say('Override accepted and logged. Do the task.')
}
log({ kind: 'blocked', summary: prompt.slice(0, 120) })
return process.stdout.write(JSON.stringify({ decision: 'block',
reason: `Gate: ${s.count} violations this week. Start with OVERRIDE: <reason> (once a day) or EMERGENCY:` }))
}
On Fridays I read the override reasons.
Step 6: Add the park-for-later valve
The hook that finally stuck was the one that never made me choose between my idea and my focus. Before it warns me, it asks: “Park this for Thursday, or do it now?” Park creates a card on my OrcStrator board that starts on its own on Thursday at 09:00, and it is never counted. The idea survives and so does the focus.

In plain Claude Code the answer to AskUserQuestion comes back inside the same turn, so the injected text carries both branches. In OrcStrator, the app I run my Claude Code sessions from, the click arrives as a new prompt, so my hook also matches the answer text. Yours does not need that.
`Otherwise do NOT start yet. Call AskUserQuestion: "Park this for Thursday, or do it now?"`,
`with options "Park it for Thursday (Recommended)" and "Do it now".`,
`Park: create ONE card scheduled for Thursday 09:00 with my ask verbatim,`,
`run: ${cmd} deferred "<summary>", and stop.`,
`Do it now: run: ${cmd} violation "<summary>", then ${WORDS[tier]}`,
Capture-only asks (“put it on the board”) skip the question. Capturing an idea is not doing the work.
Step 7: Audit by scraping your own transcripts
This step is why I know the 55% number, and it is the step most people skip. Claude Code stores every session as JSONL. Scan the assistant messages for the advisor’s name, print the prompt before each firing, and classify each firing true or false by hand.
import { readdirSync, readFileSync } from 'node:fs'
import { join } from 'node:path'
import { homedir } from 'node:os'
function* walk(d) {
for (const e of readdirSync(d, { withFileTypes: true })) {
const p = join(d, e.name)
if (e.isDirectory()) yield* walk(p)
else if (p.endsWith('.jsonl')) yield p
}
}
let last = ''
for (const f of walk(join(homedir(), '.claude', 'projects'))) {
for (const line of readFileSync(f, 'utf8').split('\n').filter(Boolean)) {
let m; try { m = JSON.parse(line) } catch { continue }
const text = [].concat(m.message?.content ?? []).map(c => c.text ?? '').join('')
if (m.type === 'user' && text) last = text.slice(0, 140)
if (m.type === 'assistant' && /^Chief of staff note:/m.test(text)) console.log(`${m.timestamp}\n asked: ${last}\n note: ${text.match(/^Chief of staff note:.*/m)[0].slice(0, 140)}`)
}
}
Run it two days after any new rule. Mark each line true or false, count, and rewrite the trigger. My rule went from 55% false to three conditions that must all hold, plus a list of things it never fires on. Logins, approvals and building the system are on that list.
What it still does not do
Honest version. Today only the Chief of staff’s client-day check is code. Six advisors have no firing log, so I have no measured hit rate for them. These are next, and none of them is built yet.
- Progress toward $50k. My daily invoice check already computes revenue every morning. The next step is to inject it into every session, so the Chief of staff works from real data and not a figure in a file.
- Time per client. The budget is 2 hours per client per week. Today the advisor enforces it by judgement. I want to measure it and inject it like the client-day counter.
- A focus score. Where the hours go each day: client, build, content.
- A firing log for all seven. A hook that records every “name note:” so each advisor gets a hit rate.
My prediction: the prompt becomes the voice, and code becomes the judgement.
Hand it to Claude Code
Open Claude Code in your own project and paste this. It reads this article and builds your version.
Read https://rodrigonask.com/claude-code-micromanager-ai-advisors and build the deterministic advisor setup it describes for me.
First ask me, one question at a time: which days are my focus days, which tool holds my task cards and how to read the open ones, who my advisors should be, and my one goal.
Then build, in this order, verifying each step before the next:
1. An "Advisory Panel" block for my CLAUDE.md with the silence, one-per-message, after-the-work and named rules, plus a separate reference file.
2. A UserPromptSubmit hook in settings.json that reads the prompt on stdin, checks the day and my open cards, and prints additionalContext. It must fail open.
3. A state file with a weekly count and an append-only JSONL log, and a --log writer mode.
4. Tiers by count: a note, yellow, red, a refusal, and a hard gate that blocks the prompt.
5. Logged exits at the gate: OVERRIDE with a reason once a day, EMERGENCY, and a credit for finishing a card.
6. A "park it for later or do it now" question before any warning. Parking creates a scheduled card and is never counted.
7. A script that scans my Claude Code transcripts for each advisor's name so I can audit the firings.
Test the hook with fake input for each tier and print the output. Never use em dashes. Do not touch anything outside my .claude folder.
When not to do this
If you work alone and nothing you do costs real money or real hours, a nagging layer is a hobby. Build it when you know the rule and keep breaking it.
If you do not know the rule yet, do not automate it. A hook enforces your judgement. It cannot supply any.
If your trigger cannot be computed (a weekday, a folder, a card count, a keyword, a sum), leave it with the model. Code makes a bad trigger consistent. It does not make it good, and I know because mine fired wrongly 27 times in 33 hours before I fixed it.
And an advisor will not decide for you. It interrupts. You still choose.
Steal the checklist
The playbook in one list:
- Code decides, the model writes.
- Numbers come from files, never from memory.
- Triggers are all-of tests with a “never on” list.
- Output uses fixed templates.
- Hard stops use a structured question, so “keep going” is a click.
- One advisor per message.
- Fail open.
- Escape hatches are logged.
- Audit like a product, two days after every new rule.
If you want the skill that writes the prompts for a build like this, I published it too. If you want a second pair of eyes on a setup, work with me 1-on-1. My context diet is how I keep the always-loaded file small enough for this to be affordable, and Safe to Close is another example of making Claude Code prove something instead of remembering it. The same agents now run my client work too: I did my week in a day, and my blog writes itself the same way. The wider operating model is in agentic marketing, and if you want me to run it for your ads, start at services. Real results from real accounts are in the case studies, and more build notes like this one live on the blog.
I share the advisor block, the hook and the audit script inside my community, Claude For Marketing, with the prompt above ready to run.
Get it in the LibraryWork with me 1-on-1What is the difference between an AI advisor and a persona prompt?
A persona prompt waits for you to ask. An advisor is a standing rule in the always-loaded instructions file that tells Claude Code when to speak without being asked: only when a costly decision is live, at the end of the reply, named, about three lines. The difference is who starts the conversation, and it is never you.
Do the advisors cost a lot of tokens?
No. The short version of the rules sits in the always-loaded file, and the long reference with each advisor’s known threads is read only right before one speaks. Because an advisor fires roughly once every few sessions, the long file is rarely read. The hook adds a few lines on client days and nothing on other days.
Can the advisors be fully deterministic?
Not yet, and not entirely. In my setup only the Chief of staff’s client-day check is code-driven: the hook computes the weekday, the open cards, the count and the tier, then injects the exact words. The model still answers one yes or no question, whether a message is client work. The other six advisors rely on the model noticing.
What stops the hard block from locking me out?
Three logged exits. Start the message with OVERRIDE and a reason, once a day. Start it with EMERGENCY for a real emergency. Or finish a client card, which buys one off-topic message. Every exit writes a line to the log, and parking the idea for Thursday is always allowed and never counted.
How do I know an advisor rule is working?
Scrape your own transcripts. List every assistant message that contains the advisor’s name, read the prompt before each one, and mark each firing true or false. My audit of one rule found 49 firings in 33 hours, 55% false, so I rewrote the trigger. Run the audit a day or two after any new rule.
