All lessons › Function F — AI Governance for Agentic Systems › Running the Programme — the CISO Office
F3.6

Saying no, and saying yes with conditions

F3
▶
Lightboard recording

Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.

Video placeholderpublishes automatically from recordings/F3.6.mp4
Risk

Conditional approval that is aspirational rather than enforceable.

Control

Autonomy promotion as an earned event with named evidence.

The lab

Write one enforceable conditional approval.

◉Use case relevance

"No" costs you the next conversation. Conditional yes — written, time-bound, tracked — is the posture that keeps you in the room, provided the conditions are enforceable and somebody actually checks them.

At CyberTravels. condition() requires all four of testable, due, owner and consequence. A condition with no consequence is a preference, and it will be recorded in the register as a control — which is worse than the flat no it was meant to avoid, because a flat no at least leaves the capability visible.

◴What this lesson is — Day 0, Day 1, Day 2

What a flat no costs in visibility, and what makes a conditional yes enforceable rather than aspirational — testable, time-bound, owned, and with a stated consequence.

Day 0 — why

Saying no is cheap and usually wrong — the capability ships anyway and you have traded influence for comfort.

Day 1 — how

Say yes with conditions that are testable, proportionate, few and owned.

Day 2 — measure

Conditions met, each evidenced by an indicator crossing a threshold. Without one, "yes with conditions" becomes "yes" at the next review.

Measure this

what to measuredenominator or target
conditions met, each evidenced by an indicator crossing a thresholdcount; without one, "yes with conditions" becomes "yes" at the next review

▦The framework, and how it works

   no                    conditional yes
   +------------+        +--------------------------------+
   | ends the   |        | scope: X only                  |
   | conversation|  vs   | until: date                    |
   | routed      |       | conditions: A, B, C            |
   | around      |       | checked by: name, monthly      |
   +------------+        +--------------------------------+

   a condition nobody checks is a "yes" with extra words

Saying no is cheap and usually wrong. The team routes around you, the capability ships anyway, and you have traded influence for a moment of comfort.

Saying yes with conditions is the job. It works when the conditions are:

  • testable — someone can check them without your involvement,
  • proportionate — tied to what the request can actually do,
  • few — five conditions get met, fifteen get negotiated away,
  • owned — each with a name and a date.

The mechanics come from the rest of the curriculum: the rung decides the governance (F3.2), the blast radius sizes the conditions (B1.4), and each condition maps to a control that already produces evidence (F1.4).

This lesson takes one genuinely uncomfortable request and gets to yes.

Anchor → F1.1. Autonomy promotion as an earned event needs named evidence, and named evidence is an indicator crossing a threshold. Without one, 'yes with conditions' becomes 'yes' at the next review.

2 · Demo — the request, assessed honestly

3 · Where it breaks — the flat no

4 · The control — five testable conditions, each owned

5 · The procedure, as a skill

The request in front of you tiers critical, because the tool it wants is irreversible and tenant-wide.

The skill first prices the flat refusal. That price is not zero: the capability ships anyway, and the visibility does not.

Then it writes conditions instead — each one testable, and each one with a date on it.

▶Real time execution as skill

The skill — skills/programme/conditional-approval-design/SKILL.md

name: conditional-approval-design
description: >-
  Tier a risky request, show what a flat refusal costs in visibility, and write
  the conditions that make a yes safe, testable and time-bound. Use when the
  answer is no and the capability will ship anyway.
allowed-tools: Read, Grep, Glob

The flat no loses the visibility and the capability ships

A refusal without conditions has a predictable outcome: the capability appears somewhere you cannot see, on somebody's personal account or inside a product already bought. A conditional yes keeps it inside the estate, and the conditions are the control — provided each one is testable and has a date.

When to use this

Any request whose straightforward answer is no, and any exception that is about to be granted informally.

Procedure

1 — Tier the request and compute its blast radius. An irreversible tenant-wide tool is what usually makes it critical, and naming that is more useful than the tier.

2 — Model the flat refusal. Where the capability goes instead, and what visibility you lose. Write it down — the point is not to argue against refusing but to price it.

3 — Write conditions that each remove a term of the risk. Gate the irreversible action, narrow the scope, cap the population, add the detection. Vague conditions — "with appropriate oversight" — are a yes with extra words.

4 — Make each condition testable. A check that would fail if the condition were not met, and who runs it. A condition nobody tests is a condition nobody keeps.

5 — Put an expiry on the approval. Conditional approvals that do not expire become permanent policy set by whoever asked first.

Example

Input — the fixture committed at the top of scripts/conditional_approval_design.py. Edit it and re-run: the buckets, counts and verdicts below are derived from it, not hard-coded.

Output — the opening lines of a real run:

request        customer-refund-agent
asks for       issue refunds up to GBP 500 without human approval
business case  42% of refund tickets are mechanical; 3.5 FTE of manual work
claimed rung   L2.5
blast radius   18  (irreversible, tenant-wide)
risk tier      high (score 8)
decision            refused
what happens        the team ships it as a 'workflow automation' outside the AI register

The run continues past this. test_skills.py executes the script on every build, with no model configured — so what CI proves is that it runs and refuses legibly, not that it prints these lines. The output above is a recorded run against a real model, and nothing re-diffs it: a model's answer is not reproducible, and this repository does not claim otherwise anywhere it can be checked.

Output contract

{
  "request": {"name": "str", "tools": ["str"], "blast": 0, "tier": "str", "irreversible": ["str"]},
  "flat_refusal": {"capability_ships_anyway": true, "visibility_lost": ["str"]},
  "conditions": [{"condition": "str", "removes": "str", "test": "str", "owner": "str"}],
  "approval": {"granted": true, "expires": "str", "review_trigger": ["str"]}
}

Failure modes

  • Refusing without pricing it. The capability ships out of sight.
  • Untestable conditions. They are a yes in a longer sentence.
  • No expiry. The exception becomes the policy.

Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.

1In your AI agent — you are taught

The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.

Install it, once. From the folder you cloned. --all does every agent it finds on your machine:

python3 scripts/install_skills.py --all --lessons F3.6

Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.

Then pick f3-6-saying-no:

agentinstall just for this agentstart it
Claude Codepython3 scripts/install_skills.py --tool claude --lessons F3.6type /f3-6-saying-no
GitHub Copilot (VS Code)python3 scripts/install_skills.py --tool copilot --lessons F3.6type /f3-6-saying-no in Chat
Cursorpython3 scripts/install_skills.py --tool cursor --lessons F3.6type / in Agent chat and search for it
Codexpython3 scripts/install_skills.py --tool agents --lessons F3.6type $f3-6-saying-no, or run /skills

2As one Python command — you just run it

No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.

python3 scripts/lesson.py F3.6

3Read the code, and the difference — nothing runs

Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.

python3 scripts/checkpoint.py --at F3.6 --out work/cybertravels
python3 scripts/checkpoint.py --at F3.6 --diff

On Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.

Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.

Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.

If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.

✓What you just proved

The request tiers critical with a blast radius of 16 from an irreversible tenant-wide tool. The flat refusal is shown to lose visibility while the capability ships anyway. Five testable conditions print with owners, mapped controls and dates; gating the refund drops the blast radius to 0 and the residual risk from 1.00 to 0.05.

✎Your turn

Take a request you refused in the last year and write the five conditions that would have made it a yes. Send them to the team that asked — they will usually accept, and you get the visibility you lost by refusing.

Skill source: skills/programme/conditional-approval-design

← PreviousF3.5 · The metrics that matter at your level