AI prompt-engineering
SaferSkills independently audited AI-prompt-engineering-Job-Search-System (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Demo version 3.0 Demo version 3.0
Demo version 4.0 Demo version 4.0
This is not a simple job search query. It is a structured, multi-source intelligence system designed to surface every active Senior Angular / Front-End Developer opportunity in the UK market — across commercial, government, and remote EU sectors, with a focus on verified, real job links and zero fabricated results.
When executed, it runs 14 sequential search steps across 13 distinct job platforms, applies strict quality control to every result, and outputs a fully formatted, horizontally scrollable HTML report with colour-coded badges, live apply links, and deduplication across sources.
Most job searches suffer from three critical failure modes:
This prompt eliminates all three by enforcing URL verification before any role is included, applying hard exclusion rules for seniority, and separating results into clearly defined sectors and location categories.
Version 1.0 | February 2026 search-job-prompt4.1.md Implements: Evaluation Loops (Principle 3) · Latency & Cost Discipline (Principle 5)
The prompt alone handles what the model searches, validates, and renders. This spec defines everything around the model: how runs are triggered, how results are compared across versions, how drift is detected, how costs are controlled, and how outputs are cached and served.
Without this layer, the prompt is a good one-shot tool. With it, it becomes a measurable, maintainable production system.
┌─────────────────────────────────────────────────────────────┐
│ TRIGGER LAYER │
│ Scheduler · Manual UI · Webhook · CLI │
└─────────────────────┬───────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────┐
│ ORCHESTRATION LAYER │
│ Step Prioritiser · Token Budget Manager · Retry Logic │
└──────┬──────────────┬──────────────────────┬────────────────┘
│ │ │
┌──────▼──────┐ ┌─────▼──────┐ ┌────────────▼───────────────┐
│ CACHE LAYER │ │ MODEL CALL │ │ FALLBACK ROUTER │
│ Static URLs │ │ Claude API │ │ 403 → flag · 404 → exclude │
│ Section 4 │ │ Prompt 4.1 │ │ Timeout → retry (max 2) │
└──────┬──────┘ └─────┬──────┘ └────────────┬───────────────┘
│ │ │
┌──────▼──────────────▼──────────────────────▼───────────────┐
│ OUTPUT LAYER │
│ Schema Parser · QC Validator · HTML Renderer │
└──────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────────────┐
│ EVAL & STORAGE LAYER │
│ Run Logger · Version Comparator · Drift Detector · DB │
└─────────────────────────────────────────────────────────────┘Every prompt execution produces a run record. Store in a lightweight database (SQLite locally, Postgres in production, or even a flat JSON file for solo use):
{
"run_id": "uuid",
"prompt_version": "4.1",
"executed_at": "ISO8601 timestamp",
"steps_attempted": 14,
"steps_successful": 10,
"steps_blocked": 3,
"steps_empty": 1,
"roles_found_raw": 42,
"roles_excluded": 11,
"roles_output": 31,
"exclusions": {
"junior_title": 4,
"no_angular": 2,
"unverified_url": 3,
"duplicate": 2,
"pipeline_ad": 0,
"other": 0
},
"confidence_distribution": {
"high": 18,
"medium": 9,
"low": 4
},
"field_completeness": {
"salary_rate_confirmed": 24,
"ir35_confirmed": 19,
"date_confirmed": 27,
"url_verified": 28
},
"tokens_used": 18420,
"cost_usd": 0.23,
"output_file": "Angular_Jobs_2026-02-18.html",
"known_gaps": ["EU remote section empty", "Hiring.cafe blocked"]
}Run these checks automatically after each execution and flag regressions:
def evaluate_run(current_run, previous_run):
checks = []
# Role count regression
delta = current_run.roles_output - previous_run.roles_output
if delta < -5:
checks.append(("WARN", f"Role count dropped by {abs(delta)} vs previous run"))
# Confidence degradation
prev_high_pct = previous_run.confidence_distribution["high"] / previous_run.roles_output
curr_high_pct = current_run.confidence_distribution["high"] / current_run.roles_output
if curr_high_pct < prev_high_pct - 0.15:
checks.append(("WARN", "High-confidence role % dropped >15pp vs previous run"))
# Exclusion rate spike
prev_excl_rate = previous_run.roles_excluded / previous_run.roles_found_raw
curr_excl_rate = current_run.roles_excluded / current_run.roles_found_raw
if curr_excl_rate > prev_excl_rate + 0.20:
checks.append(("WARN", f"Exclusion rate spiked: {curr_excl_rate:.0%} vs {prev_excl_rate:.0%}"))
# Source regression
if current_run.steps_blocked > previous_run.steps_blocked + 2:
checks.append(("WARN", "More sources blocked than previous run — check board access"))
# Cost spike
if current_run.cost_usd > previous_run.cost_usd * 1.5:
checks.append(("WARN", f"Cost spiked: ${current_run.cost_usd:.2f} vs ${previous_run.cost_usd:.2f}"))
return checksMaintain a golden dataset of known roles used to measure prompt accuracy across versions. Update monthly.
{
"test_dataset_version": "2026-02",
"cases": [
{
"id": "TC-001",
"description": "Should be INCLUDED — confirmed Senior Angular, remote UK, outside IR35",
"input_snippet": "Senior Angular Developer | TypeScript | RxJS | Fully Remote UK | Outside IR35 | £450/day",
"expected": { "excluded": false, "ir35": "outside", "work_mode": "remote_uk", "confidence": "high" }
},
{
"id": "TC-002",
"description": "Should be EXCLUDED — Junior in title",
"input_snippet": "Junior Angular Developer | Remote | £30,000",
"expected": { "excluded": true, "exclusion_reason": "junior_title" }
},
{
"id": "TC-003",
"description": "Should be EXCLUDED — Angular secondary",
"input_snippet": "Senior React Developer with some Angular experience | London | £80,000",
"expected": { "excluded": true, "exclusion_reason": "no_angular" }
},
{
"id": "TC-004",
"description": "IR35 must be TBC, not inferred — government role without explicit IR35 statement",
"input_snippet": "Front End Developer | DWP | SC Cleared | London | £500/day",
"expected": { "ir35": "tbc", "clearance": "sc_active" }
},
{
"id": "TC-005",
"description": "Salary must be null — listing says 'competitive salary'",
"input_snippet": "Senior Angular Developer | FinTech | Competitive salary | Remote UK",
"expected": { "salary_perm": null, "day_rate": null }
},
{
"id": "TC-006",
"description": "Should be low confidence — URL not fetchable",
"input_snippet": "Senior Angular Developer | Company X | [403 on fetch]",
"expected": { "source_url_verified": false, "confidence": "low" }
},
{
"id": "TC-007",
"description": "Should be EXCLUDED — EU role, no explicit UK acceptance",
"input_snippet": "Senior Angular Developer | Amsterdam | €80,000",
"expected": { "excluded": true, "exclusion_reason": "relocation_required" }
},
{
"id": "TC-008",
"description": "Should be INCLUDED as remote_eu — explicitly states UK accepted",
"input_snippet": "Senior Angular Developer | Remote EU | UK candidates welcome | €90,000",
"expected": { "excluded": false, "work_mode": "remote_eu", "sector": "eu_remote" }
}
]
}Generate this diff report whenever the prompt version changes:
## Prompt Version Comparison
Previous: v3.1 → Current: v4.1
Runs compared: [run_id_old] vs [run_id_new]
| Metric | v3.1 | v4.1 | Delta |
|-------------------------|-------|-------|--------|
| Roles output | 23 | 31 | +8 ✅ |
| High confidence % | 48% | 58% | +10pp ✅|
| Exclusion rate | 18% | 26% | +8pp ⚠️ |
| URL verified % | 74% | 90% | +16pp ✅|
| IR35 confirmed % | 61% | 71% | +10pp ✅|
| Steps blocked | 4 | 3 | -1 ✅ |
| Cost (USD) | $0.18 | $0.23 | +$0.05 ⚠️|
| Token usage | 14200 | 18420 | +4220 ⚠️|
Regressions flagged: Exclusion rate increased — investigate if new QC rules are too aggressive
Improvements: URL verification, IR35 completeness, overall role countBefore executing optional steps (Steps 9–14), estimate remaining token budget:
TOKEN_LIMITS = {
"mandatory_steps": 8000, # Steps 1–5: search + schema population
"important_steps": 4000, # Steps 6–8
"optional_steps": 3000, # Steps 9–14
"render_budget": 5000, # HTML generation
"eval_block": 500, # Self-evaluation block
"buffer": 1000, # Safety margin
"total_target": 20000 # Stay under this per run
}
def should_run_optional_steps(tokens_used_so_far):
remaining = TOKEN_LIMITS["total_target"] - tokens_used_so_far
return remaining >= TOKEN_LIMITS["optional_steps"] + TOKEN_LIMITS["render_budget"] + TOKEN_LIMITS["buffer"]| Content | Cache? | TTL | Rationale |
|---|---|---|---|
| Section 4 live search links | ✅ Yes | 7 days | URLs don't change frequently |
| Individual job listing pages | ✅ Yes | 24 hours | Listings are stable within a day |
| Search result pages (board indices) | ✅ Yes | 6 hours | Boards update multiple times/day |
| LinkedIn post searches | ❌ No | — | Too volatile; recruiter posts disappear |
| Government SC-cleared searches | ✅ Yes | 48 hours | Slower-moving market |
| Evaluation run records | ✅ Yes | Permanent | Required for trend analysis |
If running via API with streaming enabled, generate in this order to allow partial rendering in the UI:
1. Page header + source strip → renders immediately (user sees activity)
2. Section 1 — Commercial → highest value, longest section
3. Section 2 — Government → second priority
4. Section 3 — LinkedIn Spotted → lighter, faster
5. Section 4 — Live Search Links → static, no model work required
6. Appendix — Excluded roles → lowest priority, collapsed by default
7. Evaluation Report block → last, also collapsed by defaultFor teams running this for multiple role types simultaneously:
# Instead of: run sequentially (4 × full token budget)
run_search("Senior Angular Developer")
run_search("Senior React Developer")
run_search("Front End Architect")
run_search("UI Engineer")
# Do: share mandatory steps, fork at schema population
shared_results = run_mandatory_steps() # Steps 1–5 once
angular_roles = filter_and_schema(shared_results, role="angular")
react_roles = filter_and_schema(shared_results, role="react")
# etc.
# Reduces mandatory step cost by ~75% for multi-role runsSet thresholds and alert if exceeded:
COST_ALERTS = {
"per_run_warn": 0.30, # USD — warn if single run exceeds this
"per_run_stop": 0.50, # USD — halt optional steps if approaching this
"per_week_warn": 3.00, # USD — warn if weekly total exceeds this
"per_month_warn": 10.00, # USD — flag for review
}FETCH_POLICY = {
"timeout_seconds": 8, # Abort fetch after 8s — prevents blocking on slow boards
"max_retries": 2, # Retry once on timeout, then mark as blocked
"retry_delay_seconds": 3, # Wait before retry
"blocked_codes": [403, 429], # Do not retry — mark as blocked immediately
"exclude_codes": [404, 410], # Do not retry — mark as excluded
}Prompt and model behaviour can degrade over time without obvious cause. Run this check monthly or after any model version update:
| Signal | Threshold | Action |
|---|---|---|
| High-confidence role % falls below 50% | Sustained 2+ runs | Review QC rules — may be too aggressive |
| Exclusion rate exceeds 40% | Sustained 2+ runs | Review targeting rules or board changes |
unverified_url exclusions spike | +50% vs baseline | Check if boards changed URL structure |
| IR35 confirmed % falls below 50% | Sustained 2+ runs | Boards may have reduced transparency |
| Cost per run increases >30% | Single run | Check for prompt token bloat — review schema verbosity |
| Step block rate increases | 3+ consecutive runs | Boards may have updated anti-bot measures |
When Claude model version updates:
angular-job-search/
├── prompts/
│ ├── Angular_Prompt_v4_1_PromptLayer.md ← What goes to the model
│ └── changelog.md ← Prompt version history
├── system/
│ └── Angular_System_Design_Spec_v1.md ← This file
├── evals/
│ ├── test_dataset_2026-02.json ← Golden test cases
│ └── run_history.json ← All run records
├── cache/
│ └── section4_links.json ← Cached static search links
└── outputs/
├── Angular_Jobs_2026-02-18.html
└── Angular_Jobs_2026-02-25.html| Concern | Prompt Layer | System Layer |
|---|---|---|
| Search step URLs | ✅ | — |
| Role targeting rules | ✅ | — |
| Schema definition | ✅ | — |
| Input/output guardrails | ✅ | — |
| Hallucination prohibitions | ✅ | — |
| Self-evaluation block | ✅ | — |
| Run storage & history | — | ✅ |
| Version comparison | — | ✅ |
| Golden test dataset | — | ✅ |
| Drift detection | — | ✅ |
| Token budget management | — | ✅ |
| Caching | — | ✅ |
| Streaming order | — | ✅ |
| Cost monitoring | — | ✅ |
| Retry/timeout policy | — | ✅ |
| Batching (multi-role) | — | ✅ |
System Design Spec v1.0 · Pairs with Prompt v4.1 · February 2026
✅ LinkedIn Post Search Timing: Agency posts peak Mon-Wed 8-11 AM UK time — schedule searches then ✅ Agency Name Boost: Add known UK tech agencies to Boolean if targeting specific recruiters: ("Robert Walters" OR "Hays" OR "Michael Page" OR "Understanding Recruitment" OR "Computer Futures") ✅ Clearance Variants: Always include: ("SC Cleared" OR "SC Clearable" OR "SC eligibility" OR "UKSV") ✅ IR35 Filtering: For contract roles, prioritize #OutsideIR35 posts to avoid inside-IR35 contracts ✅ Tool Requirement: This prompt requires AI with live browsing/search (Perplexity, Bing Chat, ChatGPT+Browsing). Standard LLMs cannot access live LinkedIn/job data.
#### What This Tool Is This is a personal, non-commercial AI-assisted job search tool. It instructs an AI model to conduct structured searches across publicly accessible job board URLs, compile results into an organised report, and return direct links to the original source listings — functionally equivalent to a person manually visiting multiple job boards and noting relevant roles in a spreadsheet. #### Data Sources & How They Are Accessed All listings surfaced are drawn from publicly accessible, search-engine-indexed web pages, published by employers and agencies with the express intent of broad public distribution. No authenticated portals, private databases, or ATS systems are accessed. Where a URL returns an access error (403), the tool flags it with ⚠️ and does not attempt to bypass any access control. Content is retrieved via standard HTTP requests, identical to a web browser visiting a page, and is not stored, cached, or redistributed. #### Intellectual Property & Fair Use Job listings remain the intellectual property of the employers, agencies, and platforms that created them. This tool does not reproduce full job descriptions verbatim. Summarised details (title, rate, tech stack) constitute fair dealing for research and private study under the Copyright, Designs and Patents Act 1988, s.29. Every result includes a direct hyperlink to the original source, driving attribution and traffic back to the originating platform. This tool does not divert or disintermediate traffic — it directs users to original listings more efficiently: #### Always Use the Original Source The original job board listing is always the authoritative source. This report is a reference index only.
#### MIT Limitation of Liability Results are based on publicly available data at the time of search execution. Accuracy, completeness, and link availability cannot be guaranteed. Users rely on this output at their own discretion.
#### Now, don't be an stranger. Let's stay in touch!
<a href="https://github.com/leolanese" target="_blank" rel="noopener noreferrer"> <img src="https://scastiel.dev/api/image/leolanese?dark&removeLink" alt="leolanese’s GitHub image" width="600" height="314" /> </a>
##### :radio_button: Portfolio: <a href="https://www.leolanese.com" target="_blank">www.leolanese.com</a> ##### :radio_button: Linkedin: <a href="https://www.linkedin.com/in/leolanese/" target="_blank">LeoLanese</a> ##### :radio_button: Twitter: <a href="https://twitter.com/LeoLanese" target="_blank">@LeoLanese</a> ##### :radio_button: DEV.to: <a href="https://www.dev.to/leolanese" target="_blank">Tech Blog</a> ##### :radio_button: Questions / Suggestion / Recommendation: [email protected]
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.