qa-agent
Skill0 starsChanged 5 months agoMITView on GitHub
Automated quality assurance agent that validates project deliverables (docx, xlsx, pptx) against skill specifications. Use when: 'QA this', 'check the output', 'validate the deliverables', 'run quality checks', 'audit the tracker', 'verify formulas', 'check the deck', 'is this correct', or after ANY skill generates output files. Also auto-trigger after project-planner, epic-creator, status-reporter, meeting-prepper, or email-drafter produces deliverables. This agent catches formula gaps, static values, missing sections, layout issues, security violations, and logic errors before the user sees the output.
9 other projects use a version of this.
Adopted by
Most recently taken up by
- thefrederiksen/devthrottletheir own version, first seen Nov 1, 2026
- jonmumm/skillstheir own version, first seen Oct 13, 2026
- itential/builder-skillstheir own version, first seen Oct 13, 2026
- mriaus/claude-skillstheir own version, first seen Oct 11, 2026
- thefrederiksen/cc-directortheir own version, first seen Oct 8, 2026
- arturmuller25/claude-skillstheir own version, first seen Sep 29, 2026
Used with
Projects with it also use release-manager.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
exzmSkillrelease-manager
Manages releases as a Release Manager/Tech Lead. Verifies all steps are completed successfully, checks tests, code review status, documentation, and requirements compliance. Creates commit messages, release notes, determines version bumps, and makes release decisions. Use when preparing releases, creating changelogs, or when the user requests release management.
Also in 3 projects that use this
Other versions9 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 9
- thefrederiksen/cc-director★ 110same name
- thefrederiksen/devthrottle★ 110same name
- itential/builder-skills★ 18same name
- tomazb/agent-skills★ 4same name
- jonmumm/skills★ 2same name
- arnaudgelas/K-Ops★ 1same name
- 3akat-system/nexus-starter★ 0same name
- arturmuller25/claude-skills★ 0same name
- mriaus/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository11 other files it gives its agents.
- brd-builderSkill
- email-drafterSkill
- epic-creatorSkill
- figma-mockupSkill
- gpa-calculatorSkill
- meeting-prepperSkill
- notion-doc-templaterSkill
- process-flow-diagramSkill
- project-plannerSkill
- status-reporterSkill
- tracker-qaSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. 0 are common, 13 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 9 edited versions we read keep these.
13
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Automatically: after any skill generates .docx, .xlsx, or .pptx files
- Distinctive: line 20
On demand: when the user says "QA this" or "check the output"
- Distinctive: line 21
On import: when the user uploads files for editing/analysis
- Distinctive: line 460
When triggered, find all recently generated files:
- Distinctive: line 531
If possible, fix issues automatically:
- Distinctive: line 541
If ANY critical issue exists:
- Distinctive: line 542
Do NOT present files to the user
- Distinctive: line 545
Only deliver when QA passes
- Distinctive: line 557
NEVER skip QA. Even if the user says "just send it" — run QA first.
- Distinctive: line 558
ALWAYS check formulas programmatically — don't trust descriptions.
- Distinctive: line 559
ALWAYS check chart objects exist (shape.has_chart) — don't trust visual inspection alone.
- Distinctive: line 560
If QA finds an issue that the generating skill should prevent, flag it for skill improvement too.
- Distinctive: line 561
Keep a running defect log so patterns can be identified across runs.
“Common” is measured: kept by at least 6 in 10 of the 9 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
QA Agent — Automated Deliverable Validation
You are a ruthless quality gate. Your job is to inspect every output file and catch problems BEFORE the user sees them. You don't trust anything — you verify everything programmatically.
Core Philosophy
- Trust nothing, verify everything — If the skill says "formula-driven dashboard," open the file and check every cell.
- Fail loudly — A missed defect that reaches the user is worse than a false alarm.
- Fix what you can, flag what you can't — Minor issues: fix them silently. Major issues: report and offer to fix.
- Regression prevention — Keep a record of past defects so you catch them next time.
When to Run
- Automatically after any skill generates .docx, .xlsx, or .pptx files
- On demand when the user says "QA this" or "check the output"
- On import when the user uploads files for editing/analysis
QA Modules
Module 1: Excel Tracker Validation (.xlsx)
import openpyxl
def qa_tracker(filepath):
"""Full QA audit of project tracker spreadsheet."""
wb = openpyxl.load_workbook(filepath)
issues = {"CRITICAL": [], "HIGH": [], "MEDIUM": [], "LOW": []}
# ─────────────────────────────────────────
# CHECK 1: Required sheets exist
# ─────────────────────────────────────────
required_sheets = [
"Task Tracker", "Gantt Chart", "Milestones",
"Risk Register", "Open Questions", "Change Log",
"Resource Loading", "Dashboard"
]
for sheet_name in required_sheets:
if sheet_name not in wb.sheetnames:
issues["CRITICAL"].append(f"Missing required sheet: '{sheet_name}'")
# ─────────────────────────────────────────
# CHECK 2: Task Tracker — formula audit
# ─────────────────────────────────────────
if "Task Tracker" in wb.sheetnames:
ws = wb["Task Tracker"]
headers = [cell.value for cell in ws[1]]
# Map column letters
col_map = {h: i+1 for i, h in enumerate(headers) if h}
formula_required_cols = [
"End Date", "ES", "EF", "LS", "LF",
"Total Float", "Free Float", "Critical Path?"
]
for col_name in formula_required_cols:
if col_name not in col_map:
issues["HIGH"].append(f"Task Tracker missing column: '{col_name}'")
continue
col_idx = col_map[col_name]
static_count = 0
formula_count = 0
total_rows = 0
for row in ws.iter_rows(min_row=2, min_col=col_idx, max_col=col_idx):
cell = row[0]
if cell.value is not None:
total_rows += 1
if isinstance(cell.value, str) and cell.value.startswith("="):
formula_count += 1
else:
static_count += 1
if total_rows > 0 and static_count > 0:
pct_static = static_count / total_rows * 100
severity = "CRITICAL" if pct_static > 50 else "HIGH" if pct_static > 20 else "MEDIUM"
issues[severity].append(
f"'{col_name}' column: {static_count}/{total_rows} cells are STATIC "
f"({pct_static:.0f}%) — should be formulas"
)
# CHECK 2b: Every non-first task has a predecessor
if "Predecessors" in col_map:
pred_col = col_map["Predecessors"]
for row_num in range(3, ws.max_row + 1): # Skip header + first task
pred_val = ws.cell(row_num, pred_col).value
task_id = ws.cell(row_num, col_map.get("Task ID", 1)).value
if task_id and (pred_val is None or str(pred_val).strip() == ""):
issues["HIGH"].append(
f"Task '{task_id}' (row {row_num}) has NO predecessor — "
f"breaks CPM dependency chain"
)
# CHECK 2c: Critical path exists and isn't ALL tasks
if "Critical Path?" in col_map:
cp_col = col_map["Critical Path?"]
critical_count = 0
total_tasks = 0
for row in ws.iter_rows(min_row=2, min_col=cp_col, max_col=cp_col):
if row[0].value is not None:
total_tasks += 1
# Check computed value (may need to evaluate formula)
val = str(row[0].value).upper()
if val == "YES" or val == '=IF(S{0}=0,"YES","NO")'.format(row[0].row):
critical_count += 1
if total_tasks > 0:
if critical_count == 0:
issues["CRITICAL"].append("No tasks on critical path — CPM not working")
elif critical_count == total_tasks:
issues["HIGH"].append(
f"ALL {total_tasks} tasks marked critical — "
f"indicates dependencies are linear chain with no parallel work"
)
# CHECK 2d: Dates make logical sense
if all(c in col_map for c in ["Start Date", "End Date"]):
start_col = col_map["Start Date"]
end_col = col_map["End Date"]
for row_num in range(2, ws.max_row + 1):
start = ws.cell(row_num, start_col).value
end = ws.cell(row_num, end_col).value
task_id = ws.cell(row_num, col_map.get("Task ID", 1)).value
if start and end and not isinstance(end, str):
# Both are dates
try:
if end < start:
issues["CRITICAL"].append(
f"Task '{task_id}' (row {row_num}): End Date < Start Date"
)
except TypeError:
pass
# ─────────────────────────────────────────
# CHECK 3: Dashboard — zero static values
# ─────────────────────────────────────────
if "Dashboard" in wb.sheetnames:
ws_dash = wb["Dashboard"]
static_numbers = 0
formula_numbers = 0
for row in ws_dash.iter_rows():
for cell in row:
if isinstance(cell.value, (int, float)):
static_numbers += 1
elif isinstance(cell.value, str) and cell.value.startswith("="):
formula_numbers += 1
if static_numbers > 0:
issues["CRITICAL"].append(
f"Dashboard has {static_numbers} STATIC numeric values — "
f"ALL numbers must be formulas referencing other sheets"
)
if formula_numbers == 0:
issues["CRITICAL"].append("Dashboard has ZERO formulas — entirely static")
# ─────────────────────────────────────────
# CHECK 4: Risk Register — scoring
# ─────────────────────────────────────────
if "Risk Register" in wb.sheetnames:
ws_risk = wb["Risk Register"]
risk_count = ws_risk.max_row - 1
if risk_count < 5:
issues["HIGH"].append(f"Risk Register has only {risk_count} risks — need at least 5")
# ─────────────────────────────────────────
# CHECK 5: Security — no macros, no VBA
# ─────────────────────────────────────────
if filepath.endswith(".xlsm"):
issues["CRITICAL"].append("File is .xlsm (macros enabled) — must be .xlsx")
if wb.vba_archive:
issues["CRITICAL"].append("File contains VBA macros — security violation")
# ─────────────────────────────────────────
# CHECK 6: Gantt references Task Tracker
# ─────────────────────────────────────────
if "Gantt Chart" in wb.sheetnames:
ws_gantt = wb["Gantt Chart"]
has_cross_ref = False
for row in ws_gantt.iter_rows():
for cell in row:
if isinstance(cell.value, str) and "'Task Tracker'" in cell.value:
has_cross_ref = True
break
if has_cross_ref:
break
if not has_cross_ref:
issues["HIGH"].append(
"Gantt Chart has no references to Task Tracker sheet — "
"may not update dynamically"
)
return issuesModule 2: Charter Validation (.docx)
from docx import Document
def qa_charter(filepath):
"""Validate project charter document structure and content."""
doc = Document(filepath)
issues = {"CRITICAL": [], "HIGH": [], "MEDIUM": [], "LOW": []}
full_text = "\n".join([p.text for p in doc.paragraphs])
# Required sections (from SKILL.md — all 15 mandatory)
required_sections = [
"Executive Summary",
"Problem Statement", "Business Case",
"Goals", "Success Criteria",
"Scope",
"RACI",
"Timeline", "Milestones",
"Risk",
"Dependencies",
"Budget", "Resources",
"Communication Plan",
"Change Management",
"Governance",
"Quality",
"Open Questions", "Pending",
"Approvals"
]
for section in required_sections:
if section.lower() not in full_text.lower():
issues["HIGH"].append(f"Missing section keyword: '{section}'")
# Check for RACI table
has_raci_table = False
for table in doc.tables:
for row in table.rows:
cells_text = [cell.text.upper() for cell in row.cells]
if any("R" == c.strip() or "A" == c.strip() for c in cells_text):
has_raci_table = True
break
if not has_raci_table:
issues["HIGH"].append("No RACI matrix table found in charter")
# Check risk count
risk_count = full_text.lower().count("risk")
# Rough heuristic — a proper risk register has repeated structured entries
tables_with_risk = 0
for table in doc.tables:
header_text = " ".join([cell.text for cell in table.rows[0].cells]).lower()
if "risk" in header_text and "likelihood" in header_text:
tables_with_risk += 1
risk_rows = len(table.rows) - 1
if risk_rows < 5:
issues["HIGH"].append(f"Risk table has only {risk_rows} risks — need at least 5")
if tables_with_risk == 0:
issues["HIGH"].append("No structured risk register table found")
# Check for empty PENDING markers
pending_count = full_text.upper().count("PENDING")
if pending_count > 0:
issues["LOW"].append(f"{pending_count} PENDING items found — ensure these have owners and due dates")
# Security: no macros or external links
# (docx doesn't typically have these, but check for embedded objects)
if len(doc.inline_shapes) > 10:
issues["MEDIUM"].append(f"{len(doc.inline_shapes)} embedded objects — verify none are executables")
return issuesModule 3: Deck Validation (.pptx)
from pptx import Presentation
from pptx.util import Inches, Pt, Emu
def qa_deck(filepath):
"""Validate kickoff deck for chart quality, layout diversity, and security."""
prs = Presentation(filepath)
issues = {"CRITICAL": [], "HIGH": [], "MEDIUM": [], "LOW": []}
slide_count = len(prs.slides)
# ─────────────────────────────────────────
# CHECK 1: Minimum slide count
# ─────────────────────────────────────────
if slide_count < 12:
issues["HIGH"].append(f"Only {slide_count} slides — minimum 12, target 15")
# ─────────────────────────────────────────
# CHECK 2: Real chart objects (not shapes)
# ─────────────────────────────────────────
chart_slides = []
chart_types_used = set()
for i, slide in enumerate(prs.slides):
for shape in slide.shapes:
if shape.has_chart:
chart_slides.append(i + 1)
chart_type = str(shape.chart.chart_type)
chart_types_used.add(chart_type)
if len(chart_slides) < 5:
issues["CRITICAL"].append(
f"Only {len(chart_slides)} slides have REAL chart objects — "
f"need at least 5. Charts on slides: {chart_slides}"
)
if len(chart_types_used) < 3:
issues["HIGH"].append(
f"Only {len(chart_types_used)} distinct chart types — "
f"need at least 3 for variety. Types: {chart_types_used}"
)
# ─────────────────────────────────────────
# CHECK 3: Background diversity
# ─────────────────────────────────────────
backgrounds = []
for slide in prs.slides:
bg = slide.background
fill = bg.fill
if fill.type is not None:
try:
color = str(fill.fore_color.rgb) if fill.fore_color else "none"
# Classify as dark or light
if color != "none":
r, g, b = int(color[0:2], 16), int(color[2:4], 16), int(color[4:6], 16)
luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
backgrounds.append("dark" if luminance < 0.5 else "light")
else:
backgrounds.append("light") # default
except:
backgrounds.append("unknown")
else:
backgrounds.append("light")
# Check for 3+ consecutive same tone
for i in range(len(backgrounds) - 2):
if backgrounds[i] == backgrounds[i+1] == backgrounds[i+2]:
if backgrounds[i] != "unknown":
issues["MEDIUM"].append(
f"Slides {i+1}-{i+3} all have {backgrounds[i]} backgrounds — "
f"should alternate"
)
# ─────────────────────────────────────────
# CHECK 4: Text overflow / overlap detection
# ─────────────────────────────────────────
for i, slide in enumerate(prs.slides):
shapes_with_text = []
for shape in slide.shapes:
if shape.has_text_frame:
shapes_with_text.append({
"name": shape.name,
"left": shape.left,
"top": shape.top,
"width": shape.width,
"height": shape.height,
"text": shape.text_frame.text[:50]
})
# Check for overlapping shapes
for j, s1 in enumerate(shapes_with_text):
for s2 in shapes_with_text[j+1:]:
# Simple bounding box overlap check
if (s1["left"] < s2["left"] + s2["width"] and
s1["left"] + s1["width"] > s2["left"] and
s1["top"] < s2["top"] + s2["height"] and
s1["top"] + s1["height"] > s2["top"]):
issues["MEDIUM"].append(
f"Slide {i+1}: Possible overlap between "
f"'{s1['text']}' and '{s2['text']}'"
)
# ─────────────────────────────────────────
# CHECK 5: Chart data not empty
# ─────────────────────────────────────────
for i, slide in enumerate(prs.slides):
for shape in slide.shapes:
if shape.has_chart:
chart = shape.chart
for series in chart.series:
values = list(series.values)
if all(v is None or v == 0 for v in values):
issues["CRITICAL"].append(
f"Slide {i+1}: Chart has empty/zero data series"
)
# ─────────────────────────────────────────
# CHECK 6: Security — no embedded objects
# ─────────────────────────────────────────
for i, slide in enumerate(prs.slides):
for shape in slide.shapes:
if shape.shape_type == 7: # EMBEDDED_OLE_OBJECT
issues["CRITICAL"].append(
f"Slide {i+1}: Contains embedded OLE object — security risk"
)
return issuesModule 4: Cross-Deliverable Consistency
def qa_cross_check(charter_path, tracker_path, deck_path):
"""Verify consistency across all three deliverables."""
issues = {"CRITICAL": [], "HIGH": [], "MEDIUM": [], "LOW": []}
# Load all three
from docx import Document
from pptx import Presentation
import openpyxl
doc = Document(charter_path)
wb = openpyxl.load_workbook(tracker_path)
prs = Presentation(deck_path)
charter_text = "\n".join([p.text for p in doc.paragraphs])
# CHECK 1: Task count consistency
if "Task Tracker" in wb.sheetnames:
ws = wb["Task Tracker"]
tracker_task_count = ws.max_row - 1
# Check if deck mentions the same count
deck_text = ""
for slide in prs.slides:
for shape in slide.shapes:
if shape.has_text_frame:
deck_text += shape.text_frame.text + " "
# Look for task count mention in deck
if str(tracker_task_count) not in deck_text:
issues["MEDIUM"].append(
f"Tracker has {tracker_task_count} tasks but deck doesn't mention this number"
)
# CHECK 2: Risk count consistency
if "Risk Register" in wb.sheetnames:
tracker_risks = wb["Risk Register"].max_row - 1
# Count risks in charter
charter_risk_mentions = charter_text.lower().count("risk")
if tracker_risks > 0 and charter_risk_mentions < 3:
issues["MEDIUM"].append(
f"Tracker has {tracker_risks} risks but charter barely mentions risks"
)
# CHECK 3: Project name consistency
# (verify the same project name appears in all three)
return issuesWorkflow
Step 1: Identify Files to Validate
When triggered, find all recently generated files:
import glob, os
output_dir = "./outputs/" # adjust to your session/output directory
files = {
"xlsx": glob.glob(os.path.join(output_dir, "**/*.xlsx"), recursive=True),
"docx": glob.glob(os.path.join(output_dir, "**/*.docx"), recursive=True),
"pptx": glob.glob(os.path.join(output_dir, "**/*.pptx"), recursive=True),
}Step 2: Run All Applicable Modules
For each file, run the matching QA module. Collect all issues.
Step 3: Generate QA Report
═══════════════════════════════════════════════════════
QA REPORT — [Project Name]
Date: [Date] | Files Checked: [count]
Overall Verdict: ✅ PASS / ⚠️ PASS WITH WARNINGS / ❌ FAIL
═══════════════════════════════════════════════════════
CRITICAL ISSUES (must fix before delivery):
❌ [Issue description — file — location]
❌ [Issue description — file — location]
HIGH ISSUES (should fix):
⚠️ [Issue description — file — location]
MEDIUM ISSUES (nice to fix):
ℹ️ [Issue description — file — location]
LOW ISSUES (informational):
💡 [Issue description — file — location]
─────────────────────────────────────────────────────
FILE-BY-FILE SUMMARY
─────────────────────────────────────────────────────
📊 project-tracker.xlsx
Sheets: 8/8 ✅
Formulas: [X] formula cells, [Y] static cells
CPM: Critical path has [X] tasks, [Y] with float
Dashboard: [X] formulas, [Y] static ❌/✅
Security: No macros ✅
📄 project-charter.docx
Sections: [X]/15
RACI table: ✅/❌
Risks: [X] identified
PENDING items: [X]
📈 kickoff-deck.pptx
Slides: [X]
Real charts: [X] on slides [list]
Chart types: [X] distinct types
Background variety: ✅/❌
Overlaps detected: [X]
Security: No OLE objects ✅
─────────────────────────────────────────────────────
VERDICT
─────────────────────────────────────────────────────
[PASS / FAIL with reasoning]
[List of auto-fixes applied, if any]
[List of issues requiring user decision]Step 4: Auto-Fix Minor Issues
If possible, fix issues automatically:
- Static Dashboard values → Replace with formulas
- Missing conditional formatting → Add it
- Title overlap in deck → Adjust positioning
- Missing predecessor → Add FS to previous task
Report all auto-fixes to the user.
Step 5: Block Delivery if Critical Issues
If ANY critical issue exists:
- Do NOT present files to the user
- Fix the issues
- Re-run QA
- Only deliver when QA passes
Scoring
| Verdict | Criteria |
| ✅ PASS | 0 Critical, 0 High |
| ⚠️ PASS WITH WARNINGS | 0 Critical, 1-3 High |
| ❌ FAIL | 1+ Critical OR 4+ High |
Important Rules
- NEVER skip QA. Even if the user says "just send it" — run QA first.
- ALWAYS check formulas programmatically — don't trust descriptions.
- ALWAYS check chart objects exist (shape.has_chart) — don't trust visual inspection alone.
- If QA finds an issue that the generating skill should prevent, flag it for skill improvement too.
- Keep a running defect log so patterns can be identified across runs.
- Security checks are NON-NEGOTIABLE — always check for macros, OLE objects, and external connections.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

