Lesson 1 of 4
Skills
Build the extract-rules skill and turn the rulebook into verified, testable rules.
A skill turns a repeatable task into a command. You work out once how a job should be done, write it down in a file, and from then on you type /name instead of explaining it again. The file holds everything you would otherwise say from scratch each time: the steps in order, the rules, and the shape of the output.
The property that matters most is where a skill runs. Typed in your chat, it runs there, and whatever it reads lands in front of you. But a skill is only a file, so a sub-agent can load one too. This project has one of each.
The problem skills solve is consistency, not effort. Reading a rulebook is not hard. Reading it the same way every time, quoting exactly and never filling a gap from memory, is the part people get wrong. Put the method in a file and that stops being a discipline problem.
The skills in this project
Three repeatable tasks, written down once each. One you run yourself. The other two are read by agents, because rating risks the same way every time, and planning tests the same way every time, are repeatable tasks too. You build the first on this page.
What each one is for
extract-rules· Lesson 1
Turns the council's Contract Rules PDF into rules.json: one entry per testable rule, with its clause, its thresholds and its exact words. Everything later in the project cites these rules by ID.
Its partner is
verify_quotes.py, a short Python script in the same folder. A quote only counts if it matches the PDF word for word, so the skill can't just claim its quotes are right. The script proves it.It re-reads the PDF itself and checks each quote appears on the exact page the rule names. It also checks that every rule has the fields it should, that rule IDs run in order with no gaps, and that every threshold the PDF only names is declared, not guessed.
The skill runs it before it reports and fixes anything that fails. A wrong quote gets corrected from the PDF, never loosened to pass. When it prints ALL CHECKS PASSED, every rule the later steps cite is traceable to a sentence on a real page.
How it works, step by step
- 1
YouType
/extract-rulesin the Claude Code chat. - 2
SkillExtracts the text of
contract-rules.pdftwice: once as plain text, which quotes are copied from, and once keeping the page layout, so the tables can be read correctly. - 3
SkillReads every clause and writes
outputs/rules.json: one entry per testable rule, each with its clause number, its page, its £ thresholds and the rule's sentence copied word for word. - 4
ScriptRuns
verify_quotes.py. The script reads the PDF on its own and looks for every quote on the page its rule names.If anything fails ↺
It lists each failure, for example a quote with one word different, or a quote that sits on another page. The skill re-reads that page, corrects the entry in
rules.json, and runs the script again. This repeats until nothing fails.If everything passes ✓
It prints ALL CHECKS PASSED. Every quote is exactly where the rule says it is.
- 5
SkillOnly now reports back to you: how many rules it found, which thresholds the PDF names but never states, and anything odd in the document itself.
- 1
risk-assessment· Lesson 2
The method for turning facts into rated risks: how to score likelihood and impact on the council's own scale, and the rule that every rating must cite a rule, a metric or a past audit finding. It comes with a script that builds the register and refuses any rating it cannot trace. The risk-assessor sub-agent follows it.
audit-program· Lesson 4
The method for turning an approved risk into a test: the control the Rules require, a test step, and a recipe for the sample to pull. It comes with a script that draws every sample, checks every rule is accounted for and writes the planning pack. Read by the planner, a member of the agent team.
By the end of this lesson
.claude/skills/extract-rules/ with SKILL.md and verify_quotes.py, and outputs/rules.json with every quote verified against the PDF.
Steps
Why we're building it
Every later step needs to know exactly what the council's purchasing rules require. Reading the rulebook by hand takes days, and two auditors can read the same clause differently.
This skill turns the Contract Rules PDF into outputs/rules.json: one entry per testable rule, with its clause, its £ thresholds, the action it requires, who must approve, and the rule's own words quoted exactly.
The rest of the system depends on it. The spend-analyst takes its thresholds from this file, and every risk in the register cites a rule by its ID.
It's a skill rather than a sub-agent because you run it yourself, once, with a slash command.
How to build it
The skill is fitted to the real document: its clause numbering, its appendix tables and its page footers.
A verifier checks that every quote appears word for word on the page it cites. Thresholds the PDF only names, without a figure, stay empty until an auditor supplies them, so nothing is guessed.
Choose one of these two options.
Option 1: ask Claude Code to write the skill
Claude reads the PDF first and writes a skill that fits it. This is how the skill in this guide was made.
Build the extract-rules skill described in CLAUDE.md. Before writing it, extract the text of data/contract-rules.pdf with pdftotext, both plain and with -layout, and read it, so the skill fits the document's real structure. Requirements: - SKILL.md frontmatter has only name and description. - One entry per testable rule, with clause, page, type, scope, thresholds as printed (GBP integers plus inclusive/exclusive flags), required action, approver, evidence source and a verbatim quote. - Thresholds the PDF only names, such as "the Threshold", go in a top-level parameters object with null values. Never fill them in from memory. - Every clause ends up in either rules or excluded, with a one-line reason for each exclusion. - Contradictions and drafting gaps in the PDF go in a notes list. - Write verify_quotes.py next to SKILL.md. It checks the schema, sequential rule IDs, declared parameters, and that every quote appears on its stated PDF page. Test the verifier on a fixture with deliberate errors before you finish. Don't run the skill on the PDF yet.
Option 2: copy the finished skill
Use the exact skill from this guide. It has two files, and the skill needs both: SKILL.md runs verify_quotes.py to check every quote.
- In VS Code's left-hand panel, right-click
.claude/skillsand choose New Folder. Name itextract-rules. - Right-click
extract-rules, choose New File, and name itSKILL.md. Copy the first file below into it and save. - Do the same for a file named
verify_quotes.py, using the second file below.
--- name: extract-rules description: Extract every testable rule from data/contract-rules.pdf into outputs/rules.json with clause references, thresholds, approvers and verified verbatim quotes. --- # extract-rules Turn the council's Contract Rules PDF into a machine-readable list of testable rules. Downstream, `spend-analyst` parameterises its tests from the threshold values in this file and `risk-assessment` cites rules by `rule_id`, so every number and quote here must be exact and traceable.
#!/usr/bin/env python3
"""Verify outputs/rules.json against the Contract Rules PDF.
Usage: verify_quotes.py RULES_JSON PDF_PATH
Checks
1. JSON parses and has the expected top-level keys.
2. Every rule has the required fields with the right types.
3. rule_ids are unique and sequential (CPR-01, CPR-02, ...).
4. Every threshold_*_ref names a key in `parameters`.
5. Every verbatim_quote appears in the PDF text after light normalisation
(whitespace collapsed, page footers and bullet glyphs removed, quote/dashWhat to expect
- Either way,
.claude/skills/extract-rules/holdsSKILL.mdandverify_quotes.py. - With option 1, Claude also shows the verifier passing real quotes and catching the planted errors, such as a wrong page and a one-word misquote.
What you should see after step 2.1
Explorer
⌄ procurement-audit-planner
- ⌄.claude
- ›agents
- ⌄skills
- ⌄extract-rules
- SKILL.mdnew
- verify_quotes.pynew
- ›.venv
- ›data
- ›outputs
- ›scripts
- CLAUDE.md
- requirements.txt
The skill walks every clause, classifies it, writes rules.json, and runs the verifier until every quote passes.
/extract-rules
What to expect
- Counts of rules by type, the excluded count, and the threshold parameters still waiting for values.
- Notes about the document itself, such as two clauses that disagree on whether contract values include VAT.
> /extract-rulesChecking preconditions, then extracting the text twice.● Bash(pdftotext data/contract-rules.pdf … && pdftotext -layout …)15 pages● Write(outputs/rules.json)● Bash(python3 .claude/skills/extract-rules/verify_quotes.py outputs/rules.json data/contract-rules.pdf)rules.json: 66 rules, 92 excluded, 4 parametersALL CHECKS PASSED (66 rules, 92 excluded, 0 warning(s))Four statutory threshold parameters are null. The PDF names them but never gives the figures.
What you should see after step 2.2
Explorer
⌄ procurement-audit-planner
- ›.claude
- ›.venv
- ›data
- ⌄outputs
- rules.jsonnew
- ›scripts
- CLAUDE.md
- requirements.txt
What you have built so far
2 of 11 filesThe rulebook is now a list of testable rules that later steps cite by ID, with every quote checked word for word against the PDF. Next you turn those rules on the council's payments.
- ✓ builtThe briefCLAUDE.md
- ✓ builtTestable rulesoutputs/rules.json
- not yetSpend indicatorsoutputs/analytics.json
- not yetAudit historyoutputs/history.json
- not yetRisk registeroutputs/risk-register.md
- not yetAuditor decisionsoutputs/auditor-comments.md
- not yetPlanning memooutputs/planning-memo.md
- not yetRisk-control matrixoutputs/risk-control-matrix.md
- not yetAudit programoutputs/audit-program.md
- not yetChallengesoutputs/challenges.md
- not yetQA review and sign-offoutputs/review.md