
Chro Advisor
- 107 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
chro-advisor is a Claude agent skill that delivers CHRO-level hiring, compensation, org design, performance, and retention frameworks for developers who lead growing engineering organizations.
About
chro-advisor is a Claude agent skill in borghei/claude-skills (version 2.0.0) that packages chief-people-officer frameworks for workforce planning, compensation bands, org design, performance calibration, and retention strategy. The SKILL.md defines six framework areas and ships three Python CLI tools—retention_risk_scorer.py, headcount_planner.py, and comp_band_analyzer.py—for scoring attrition risk, modeling fully-loaded hiring costs, and auditing compa-ratios. It includes decision trees for hiring justification, equity grant bands by funding stage, spans-of-control tables, PIP structures, and board-level people metrics with explicit targets such as sub-10% regrettable attrition and 45-day time-to-fill. Engineering managers and tech leads reach for chro-advisor when building headcount plans, designing salary bands, restructuring teams, or responding to attrition spikes without outsourcing HR strategy. Deliverables include headcount plans, comp frameworks, org proposals, retention analyses, and performance review templates, with integration hooks to sibling C-level advisor skills for CFO budget alignment.
- Bundles 3 Python CLIs: retention_risk_scorer, headcount_planner, comp_band_analyzer
- Defines 6 framework areas: people-strategy, comp, org-design, performance, retention, workforce-planning
- Equity grant tables for Seed through Series C+ with level bands L1–L5 and M1–M5
- Board metrics with targets: <10% regrettable attrition, <45-day time-to-fill, eNPS >30
- SKILL.md version 2.0.0 with workforce planning decision tree and PIP structure
Chro Advisor by the numbers
- 107 all-time installs (skills.sh)
- Ranked #1,334 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/borghei/claude-skills --skill chro-advisorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 107 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
How do you model headcount plans and comp bands for a scaling team?
Run chro-advisor to produce a headcount plan with fully-loaded cost projections and retention risk scores before your next hiring committee.
Who is it for?
Engineering managers and tech leads at Series A–C companies who own hiring plans, comp bands, and org structure without a dedicated CHRO.
Skip if: Developers who only need employment-law compliance, payroll operations, or individual performance counseling rather than org-level people strategy.
When should I use this skill?
The user mentions headcount planning, salary bands, org design, regrettable attrition, performance calibration, or CHRO/people strategy for a scaling team.
What you get
Headcount plans, compensation band frameworks, org chart proposals, retention risk scores, and performance review templates.
- headcount plan
- comp band framework
- retention risk report
By the numbers
- SKILL.md version 2.0.0 with 6 named framework areas
- Bundles 3 Python CLI scripts for retention, headcount, and comp analysis
- Defines 6-factor retention risk scoring matrix and 5-tier performance rating distribution
Files
CHRO Advisor
People strategy and operational HR frameworks for business-aligned hiring, compensation, org design, performance management, and culture that scales. The CHRO translates business goals into people requirements and ensures the organization has the talent, structure, and culture to execute.
Keywords
CHRO, chief people officer, HR, human resources, people strategy, hiring plan, headcount planning, talent acquisition, recruiting, compensation, salary bands, equity, org design, organizational design, career ladder, title framework, retention, performance management, culture, engagement, remote work, hybrid, spans of control, succession planning, attrition, workforce planning, people analytics, eNPS, onboarding, offboarding, DEI, employer brand
---
Quick Start
Workforce Planning Decision Tree
START: Business goal identified
|
v
[Can existing team deliver this goal?]
|
+-- YES --> [Is current capacity sustainable?]
| |
| +-- YES --> No hiring needed. Optimize.
| +-- NO --> Hire for sustainability (backfill/support)
|
+-- NO --> [Is this a skill gap or capacity gap?]
|
+-- SKILL GAP --> [Can we develop internally in < 90 days?]
| |
| +-- YES --> Train/develop. No hire.
| +-- NO --> Hire specialist.
|
+-- CAPACITY GAP --> [Is this temporary or permanent?]
|
+-- TEMPORARY --> Contract/agency
+-- PERMANENT --> Full-time hire with business case---
Core Responsibilities
1. Workforce Planning and Headcount
Every hire needs a business case. "We need more people" is not a business case.
Hiring Justification Framework
| Question | Required Answer |
|---|---|
| What revenue or risk does this role address? | Specific dollar amount or risk description |
| What happens if we don't fill this in 90 days? | Concrete impact statement |
| Can existing team absorb this with re-prioritization? | Yes/No with explanation |
| What's the fully-loaded cost (salary + benefits + equity + tools + overhead)? | Dollar amount |
| What's the expected ramp time to full productivity? | Weeks/months |
| Who will manage this person? | Named manager with capacity |
Headcount Planning by Stage
| Stage | Team Size | CHRO Focus | Hiring Speed |
|---|---|---|---|
| Pre-seed | 1-5 | Founders hire directly | 1-2/quarter |
| Seed | 5-15 | First structured interviews, no HR person yet | 2-4/quarter |
| Series A | 15-40 | First People hire, comp bands, career ladder v1 | 4-8/quarter |
| Series B | 40-100 | CHRO or VP People, full hiring process, HRIS | 8-20/quarter |
| Series C | 100-250 | People team (3-5), manager training, performance system | 15-40/quarter |
| Growth | 250+ | Full people function, analytics, L&D, total rewards | 30+/quarter |
2. Compensation Design
Compensation Band Architecture
Level Framework:
IC Track Management Track
--------- ----------------
L1: Junior/Associate --
L2: Mid-level --
L3: Senior M1: Manager (first-time)
L4: Staff/Principal M2: Senior Manager
L5: Distinguished/Fellow M3: Director
-- M4: VP
-- M5: SVP/C-levelBand Construction Method
| Step | Action | Data Source |
|---|---|---|
| 1 | Define levels with clear competency criteria | Internal role descriptions |
| 2 | Benchmark each level against market | Levels.fyi, Pave, Radford, Option Impact |
| 3 | Set band width (typically 20-30% spread) | Market data + internal equity |
| 4 | Position band midpoint at target percentile | P50 for cash, P50-P75 for total comp |
| 5 | Define equity bands per level | Stage-appropriate equity calculator |
| 6 | Set promotion criteria between levels | Performance + scope + impact |
Total Compensation Components
| Component | Purpose | Refresh Cadence |
|---|---|---|
| Base salary | Market-rate cash compensation | Annual review |
| Annual bonus | Performance-linked variable pay | Annual (if applicable) |
| Equity (options/RSUs) | Long-term alignment and retention | Initial grant + annual refresh |
| Benefits | Health, 401k, perks | Annual review |
| Signing bonus | Competitive offer sweetener | One-time |
Equity Grant Guidelines by Stage
| Stage | IC Hire (L2-L3) | Senior Hire (L4-L5) | VP/C-Level |
|---|---|---|---|
| Seed | 0.25-1.0% | 1.0-2.5% | 2.0-5.0% |
| Series A | 0.05-0.25% | 0.25-0.75% | 0.5-2.0% |
| Series B | 0.01-0.10% | 0.10-0.30% | 0.25-1.0% |
| Series C+ | 0.005-0.05% | 0.05-0.15% | 0.10-0.50% |
3. Organizational Design
Spans of Control Guidelines
| Role Type | Optimal Span | Warning Signs |
|---|---|---|
| IC Manager (engineering) | 5-8 direct reports | > 10: no coaching time. < 4: unnecessary layer |
| IC Manager (non-eng) | 6-10 direct reports | > 12: overwhelmed. < 5: manager inflation |
| Manager of Managers | 4-7 direct reports | > 8: can't support managers. < 3: too many layers |
| VP/Director | 5-8 direct reports | > 10: strategic thinking suffers |
When to Add Management Layers
TRIGGER: Team growing past threshold
|
v
[Current span of control > optimal?]
|
+-- NO --> Don't add layer. Resist the urge.
+-- YES --> [Is there a strong internal candidate?]
|
+-- YES --> Promote from within (faster, culture-preserving)
+-- NO --> [Is external hire justified?]
|
+-- YES --> Hire manager with 90-day expectations
+-- NO --> Split team instead of adding layerOrg Design Anti-Patterns
| Anti-Pattern | Symptom | Fix |
|---|---|---|
| Title inflation | Everyone is a "Head of" at 20 people | Standardized level framework |
| Shadow org | Real decisions made outside official structure | Align authority with accountability |
| Matrix chaos | Every person has 3 reporting lines | One clear manager, dotted lines documented |
| Founder bottleneck | All decisions flow through founder | Delegation framework (see founder-coach) |
| Empire building | Managers hire to grow team, not to deliver | Tie headcount to business outcomes |
4. Performance Management
Calibrated Performance Framework
| Rating | Label | Distribution Target | Action |
|---|---|---|---|
| 5 | Exceptional | 5-10% | Accelerated promotion, significant equity refresh, retention bonus |
| 4 | Exceeds Expectations | 20-25% | Above-market raise, stretch assignment, mentor role |
| 3 | Meets Expectations | 50-60% | Market adjustment, development plan, new challenges |
| 2 | Needs Improvement | 10-15% | PIP with 60-day milestones, weekly manager check-ins |
| 1 | Underperforming | 2-5% | Exit conversation or immediate role change |
Performance Review Cadence
| Activity | Frequency | Owner | Participants |
|---|---|---|---|
| 1:1 meetings | Weekly | Manager | Manager + direct report |
| Goal check-in | Monthly | Manager | Manager + direct report |
| Peer feedback collection | Quarterly | People team | Cross-functional peers |
| Performance review | Semi-annual | Manager + People | Manager, report, skip-level |
| Calibration session | Semi-annual | People team | All managers at same level |
| Promotion committee | Semi-annual | People + Leadership | Committee of L4+ leaders |
PIP (Performance Improvement Plan) Structure
| Element | Requirement |
|---|---|
| Specific gaps | Observable behaviors, not vague criticism |
| Measurable goals | 3-5 targets with success criteria |
| Timeline | 30-60 days maximum |
| Support offered | Training, mentoring, resources |
| Check-in cadence | Weekly minimum |
| Clear outcome | What happens if goals are met vs. not met |
| Documentation | Written, signed, filed |
5. Retention Strategy
Retention Risk Assessment Matrix
| Factor | Low Risk (1) | Medium Risk (2) | High Risk (3) |
|---|---|---|---|
| Comp competitiveness | Above P50 | At P50 | Below P50 |
| Manager relationship | Strong trust | Adequate | Friction or distrust |
| Career growth | Clear path, progressing | Path exists, slow progress | No visible path |
| Engagement | High eNPS, advocates | Neutral | Disengaged, passive |
| Tenure | < 1 year or > 3 years | 1-2 years | 18-24 months (cliff danger) |
| External demand | Low market demand | Moderate | Hot market, recruiters active |
Total score 6-8: Low risk. Monitor quarterly. Total score 9-13: Medium risk. Proactive retention conversation needed. Total score 14-18: High risk. Immediate intervention required.
Retention Intervention Ladder
Risk Level: LOW (6-8)
--> Standard: competitive comp, regular 1:1s, career conversations
Risk Level: MEDIUM (9-13)
--> Proactive: skip-level conversation, comp review, stretch project
--> Timeline: act within 30 days of identification
Risk Level: HIGH (14-18)
--> Urgent: retention package (comp + equity + role change), CEO involvement
--> Timeline: act within 7 days of identification
--> If departure: structured exit interview, knowledge transfer plan---
People Metrics Dashboard
Tier 1: Board-Level Metrics (Monthly)
| Metric | Target | Red Flag | Data Source |
|---|---|---|---|
| Regrettable attrition (annualized) | < 10% | > 15% | HRIS |
| eNPS score | > 30 | < 0 | Quarterly survey |
| Time to fill (critical roles) | < 45 days | > 90 days | ATS |
| Offer acceptance rate | > 85% | < 70% | ATS |
| Revenue per employee | Growing QoQ | Declining | Finance + HRIS |
Tier 2: Leadership Metrics (Weekly)
| Metric | Target | Action Trigger |
|---|---|---|
| Open requisitions | Per plan | > 120% of plan = capacity strain |
| 90-day voluntary turnover | < 5% | > 8% = onboarding/hiring problem |
| Manager effectiveness score | > 3.8/5 | < 3.5 = management development needed |
| % employees within comp band | > 90% | < 80% = band recalibration needed |
| Internal promotion rate | > 25% | < 15% = career development gap |
Tier 3: Operational Metrics (Daily/Weekly)
| Metric | Purpose |
|---|---|
| Pipeline by role (candidates per stage) | Hiring velocity tracking |
| Interviewer load (interviews per person per week) | Prevent interviewer burnout |
| Offer-to-close time | Process efficiency |
| Compa-ratio distribution | Compensation equity |
| Training completion rate | Compliance and development |
---
Red Flags
- Attrition spikes with exit interviews naming the same manager -- manager problem, not culture problem
- Comp bands not refreshed in 18+ months -- losing candidates and retaining the wrong people
- No career ladder exists -- top performers leave at 18-24 months
- Hiring without written job scorecard -- inconsistent decisions, bias risk
- Performance reviews happen once a year only -- problems fester
- Equity refreshes limited to executives -- key ICs become flight risks
- Time to fill > 90 days for critical roles -- process is broken or comp is wrong
- eNPS below 0 -- structural problem, not a morale issue
- More than 3 org layers between IC and CEO at < 50 people -- over-managed
- HR team ratio > 1:100 (too lean) or < 1:40 (too heavy) -- right-size the function
- No structured onboarding beyond day 1 -- 90-day attrition will spike
---
Integration with C-Suite
| When... | CHRO Works With... | To... |
|---|---|---|
| Headcount planning | CFO (cfo-advisor) | Model fully-loaded cost, secure budget |
| Hiring timing | COO (coo-advisor) | Align with operational capacity and project timelines |
| Engineering hiring | CTO (cto-advisor) | Define technical scorecards, level expectations |
| Revenue team scaling | CRO (cro-advisor) | Quota coverage modeling, ramp time projections |
| Board reporting | CEO (ceo-advisor) | People KPIs, attrition risk narrative, culture health |
| Equity grants | CFO + Board | Dilution modeling, option pool refresh |
| Culture programs | Culture Architect (culture-architect) | Behavioral anchors, engagement programs |
| Org restructuring | CEO + COO | Change management, communication plan |
| Founder development | Founder Coach (founder-coach) | Leadership style evolution, delegation |
---
Proactive Triggers
Surface these without being asked when detected:
- Key person approaching equity cliff with no refresh plan -- retention risk, act immediately
- Hiring plan exists but no comp bands defined -- will overpay or lose candidates
- Team growing past 25-30 with no manager layer -- org strain imminent
- No performance review cycle -- underperformers hide, top performers leave
- Regrettable attrition > 10% -- mandatory exit interview analysis
- Manager-to-IC ratio outside 1:5-1:10 range -- org structure review needed
- No succession plan for any leadership role -- single-point-of-failure risk
- Offer acceptance rate drops below 75% -- comp or process problem
---
Output Artifacts
| Request | Deliverable |
|---|---|
| "Build a hiring plan" | Headcount plan: roles, timing, cost, ramp model, business case per role |
| "Set up comp bands" | Compensation framework: levels, bands, equity, benchmarks, refresh policy |
| "Design our org" | Org chart proposal: spans, layers, transition plan, timeline |
| "We're losing people" | Retention analysis: risk scores, root causes, intervention plan per person |
| "People board section" | Board slide: headcount, attrition, hiring velocity, engagement, top risks |
| "Performance review setup" | Performance framework: ratings, calibration, review cadence, templates |
| "Remote work policy" | Policy document: expectations, tools, communication norms, exceptions |
---
Tool Reference
retention_risk_scorer.py
Scores employee retention risk across 6 factors (comp, manager, career, engagement, tenure, market demand). Generates prioritized intervention plans and identifies org-level patterns.
# Run with demo data
python scripts/retention_risk_scorer.py
# From JSON with employee data
python scripts/retention_risk_scorer.py --input employees.json
# JSON output
python scripts/retention_risk_scorer.py --jsonheadcount_planner.py
Models hiring plans with fully-loaded cost projections, ramp timelines, ROI per role, and quarterly budget impact.
# Run with demo plan
python scripts/headcount_planner.py
# From JSON hiring plan
python scripts/headcount_planner.py --input hiring_plan.json
# JSON output
python scripts/headcount_planner.py --jsoncomp_band_analyzer.py
Analyzes compensation equity: compa-ratios, band positioning, pay equity flags, and adjustment recommendations with budget impact.
# Run with demo data
python scripts/comp_band_analyzer.py
# From JSON with bands and employees
python scripts/comp_band_analyzer.py --input comp_data.json
# JSON output
python scripts/comp_band_analyzer.py --json---
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
| Attrition spikes with exit interviews naming the same manager | Manager problem, not culture problem | Investigate the specific manager; provide coaching or make a change |
| Comp bands not refreshed in 18+ months | Market has moved; losing candidates and retaining wrong people | Benchmark against Levels.fyi/Pave/Radford; update bands quarterly for hot roles |
| Top performers leave at 18-24 months | No career ladder; equity cliff approaching with no refresh | Build career ladder with clear criteria; implement annual equity refresh program |
| Offer acceptance rate drops below 75% | Comp is wrong, process is too slow, or candidate experience is poor | Audit rejected offers for reason; benchmark comp; measure time-to-offer |
| Performance reviews happen once a year and problems fester | Review cadence too infrequent; no continuous feedback culture | Implement weekly 1:1s, monthly goal check-ins, semi-annual formal reviews |
| eNPS drops below 0 | Structural problem, not a morale event | Deep-dive survey results by department and manager; address root causes |
---
Success Criteria
- Regrettable attrition below 10% annualized (measured monthly, reported to board quarterly)
- eNPS score above 30 (surveyed quarterly with 80%+ participation)
- Time to fill for critical roles under 45 days (measured from req open to offer accepted)
- Offer acceptance rate above 85% (tracked in ATS, reviewed monthly)
- 90%+ of employees within their compensation band (measured quarterly via comp_band_analyzer.py)
- Internal promotion rate above 25% (promotions / total role fills)
- Zero key-person departures without a succession plan activated (retention_risk_scorer.py identifies risk)
---
Scope & Limitations
In Scope: Workforce planning, compensation design, org structure, performance management, retention strategy, career ladders, people analytics, headcount modeling, comp band analysis.
Out of Scope: Employment law advice, immigration processing, payroll operations, benefits administration, workers' compensation claims, union negotiations, individual employee counseling.
Limitations: Retention risk scoring relies on manager assessments which may have bias. Comp band analysis uses provided market data -- accuracy depends on benchmark quality. Headcount planner uses linear cost projections that don't account for signing bonuses, relocation, or variable compensation. Pay equity analysis requires gender/demographic data which may not be available.
---
Integration Points
| Skill | Integration |
|---|---|
cfo-advisor | Headcount budget modeling; fully-loaded cost for financial planning |
ceo-advisor | People KPIs for board reporting; attrition risk narrative |
coo-advisor | Hiring timing aligned with operational capacity |
cto-advisor | Engineering hiring scorecards; technical leveling |
cro-advisor | Revenue team quota coverage modeling; sales ramp projections |
culture-architect | Behavioral anchors for performance reviews; engagement programs |
founder-coach | Founder leadership style evolution; delegation frameworks |
change-management | People impact assessment for reorgs; communication sequencing |
#!/usr/bin/env python3
"""
Compensation Band Analyzer - Analyze compensation equity and band health.
Evaluates compa-ratios, identifies pay equity issues, flags employees
outside bands, and generates comp adjustment recommendations.
"""
import argparse
import json
import sys
from datetime import datetime
def analyze_bands(data: dict) -> dict:
"""Analyze compensation bands and employee positioning."""
bands = data.get("bands", {})
employees = data.get("employees", [])
results = {
"timestamp": datetime.now().isoformat(),
"band_summary": {},
"employee_analysis": [],
"equity_flags": [],
"adjustment_recommendations": [],
"budget_impact": {},
"recommendations": [],
}
total_adjustment_cost = 0
outside_band = 0
below_midpoint = 0
for emp in employees:
name = emp.get("name", "")
level = emp.get("level", "L3")
current_comp = emp.get("base_salary", 0)
department = emp.get("department", "")
tenure_years = emp.get("tenure_years", 0)
performance = emp.get("performance_rating", 3)
gender = emp.get("gender", "")
role_type = emp.get("role_type", "ic") # ic or management
# Get band
band = bands.get(level, {})
band_min = band.get("min", 0)
band_mid = band.get("mid", 0)
band_max = band.get("max", 0)
if band_mid == 0:
continue
# Compa-ratio
compa_ratio = current_comp / band_mid if band_mid > 0 else 0
# Position in band
if current_comp < band_min:
band_position = "Below Band"
outside_band += 1
adjustment = band_min - current_comp
elif current_comp > band_max:
band_position = "Above Band"
outside_band += 1
adjustment = 0
elif compa_ratio < 0.90:
band_position = "Low in Band"
below_midpoint += 1
adjustment = band_mid * 0.95 - current_comp if performance >= 3 else 0
elif compa_ratio > 1.10:
band_position = "High in Band"
adjustment = 0
else:
band_position = "Midpoint"
adjustment = 0
adjustment = max(0, round(adjustment))
total_adjustment_cost += adjustment
emp_result = {
"name": name,
"level": level,
"department": department,
"current_salary": current_comp,
"band_min": band_min,
"band_mid": band_mid,
"band_max": band_max,
"compa_ratio": round(compa_ratio, 2),
"band_position": band_position,
"performance": performance,
"tenure_years": tenure_years,
"recommended_adjustment": adjustment,
"gender": gender,
}
results["employee_analysis"].append(emp_result)
if adjustment > 0:
results["adjustment_recommendations"].append({
"name": name,
"level": level,
"current": current_comp,
"recommended": current_comp + adjustment,
"adjustment": adjustment,
"reason": f"{band_position} (compa-ratio: {compa_ratio:.2f})",
"priority": "Immediate" if band_position == "Below Band" else "Next cycle",
})
# Pay equity analysis
by_level_gender = {}
for emp in results["employee_analysis"]:
key = (emp["level"], emp["gender"])
if key not in by_level_gender:
by_level_gender[key] = []
by_level_gender[key].append(emp["compa_ratio"])
# Check for gender gaps
levels_seen = set(emp["level"] for emp in results["employee_analysis"])
for level in levels_seen:
male_ratios = by_level_gender.get((level, "M"), [])
female_ratios = by_level_gender.get((level, "F"), [])
if male_ratios and female_ratios:
avg_m = sum(male_ratios) / len(male_ratios)
avg_f = sum(female_ratios) / len(female_ratios)
gap = abs(avg_m - avg_f)
if gap > 0.05:
results["equity_flags"].append({
"level": level,
"type": "Gender pay gap",
"detail": f"Avg compa-ratio M={avg_m:.2f} vs F={avg_f:.2f} (gap: {gap:.2f})",
"severity": "High" if gap > 0.10 else "Medium",
})
# Band summary
for level, band in bands.items():
level_emps = [e for e in results["employee_analysis"] if e["level"] == level]
if level_emps:
avg_compa = sum(e["compa_ratio"] for e in level_emps) / len(level_emps)
results["band_summary"][level] = {
"employees": len(level_emps),
"band_range": f"${band.get('min', 0):,.0f} - ${band.get('max', 0):,.0f}",
"midpoint": band.get("mid", 0),
"avg_compa_ratio": round(avg_compa, 2),
"below_band": sum(1 for e in level_emps if e["band_position"] == "Below Band"),
"above_band": sum(1 for e in level_emps if e["band_position"] == "Above Band"),
}
# Budget impact
results["budget_impact"] = {
"total_adjustment_cost_annual": total_adjustment_cost,
"employees_needing_adjustment": len(results["adjustment_recommendations"]),
"employees_outside_band": outside_band,
"employees_below_midpoint": below_midpoint,
"pct_within_band": round((len(employees) - outside_band) / len(employees) * 100) if employees else 0,
}
# Recommendations
if outside_band > 0:
results["recommendations"].append(f"{outside_band} employee(s) outside compensation bands. Adjust to maintain equity.")
if results["equity_flags"]:
results["recommendations"].append(f"{len(results['equity_flags'])} pay equity flag(s) detected. Review and remediate.")
if total_adjustment_cost > 0:
results["recommendations"].append(f"Total adjustment budget needed: ${total_adjustment_cost:,.0f}/year.")
return results
def format_text(results: dict) -> str:
"""Format as human-readable report."""
bi = results["budget_impact"]
lines = [
"=" * 75,
"COMPENSATION BAND ANALYSIS",
"=" * 75,
f"Date: {results['timestamp'][:10]}",
f"Within Band: {bi['pct_within_band']}% | Outside: {bi['employees_outside_band']} | Adjustments Needed: {bi['employees_needing_adjustment']}",
f"Adjustment Budget: ${bi['total_adjustment_cost_annual']:,.0f}/year",
"",
f"{'Name':<18} {'Level':<5} {'Salary':>10} {'Mid':>10} {'Compa':>6} {'Position':<14} {'Adjust':>8}",
"-" * 75,
]
for e in sorted(results["employee_analysis"], key=lambda x: x["compa_ratio"]):
adj = f"${e['recommended_adjustment']:,.0f}" if e["recommended_adjustment"] > 0 else "-"
icon = "[R]" if e["band_position"] in ["Below Band"] else "[Y]" if e["band_position"] in ["Low in Band", "Above Band"] else "[G]"
lines.append(
f"{e['name']:<18} {e['level']:<5} ${e['current_salary']:>9,.0f} ${e['band_mid']:>9,.0f} "
f"{e['compa_ratio']:>5.2f} {icon} {e['band_position']:<12} {adj:>8}"
)
if results["equity_flags"]:
lines.extend(["", "PAY EQUITY FLAGS:"])
for ef in results["equity_flags"]:
lines.append(f" [{ef['severity']}] {ef['level']}: {ef['detail']}")
if results["recommendations"]:
lines.extend(["", "RECOMMENDATIONS:"])
for r in results["recommendations"]:
lines.append(f" -> {r}")
lines.extend(["", "=" * 75])
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Analyze compensation bands and equity")
parser.add_argument("--input", "-i", help="JSON file with comp data")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
if args.input:
with open(args.input) as f:
data = json.load(f)
else:
data = {
"bands": {
"L2": {"min": 90000, "mid": 110000, "max": 130000},
"L3": {"min": 130000, "mid": 155000, "max": 180000},
"L4": {"min": 170000, "mid": 200000, "max": 230000},
"M1": {"min": 140000, "mid": 165000, "max": 190000},
"M2": {"min": 170000, "mid": 200000, "max": 230000},
},
"employees": [
{"name": "Alice Chen", "level": "L4", "base_salary": 195000, "department": "Engineering", "tenure_years": 2, "performance_rating": 4, "gender": "F"},
{"name": "Bob Smith", "level": "L4", "base_salary": 215000, "department": "Engineering", "tenure_years": 3, "performance_rating": 4, "gender": "M"},
{"name": "Carol Davis", "level": "L3", "base_salary": 125000, "department": "Engineering", "tenure_years": 1, "performance_rating": 3, "gender": "F"},
{"name": "Dan Lee", "level": "L3", "base_salary": 160000, "department": "Engineering", "tenure_years": 2, "performance_rating": 3, "gender": "M"},
{"name": "Eve Martinez", "level": "L2", "base_salary": 88000, "department": "Marketing", "tenure_years": 1, "performance_rating": 3, "gender": "F"},
{"name": "Frank Johnson", "level": "M1", "base_salary": 175000, "department": "Engineering", "tenure_years": 4, "performance_rating": 5, "gender": "M", "role_type": "management"},
],
}
results = analyze_bands(data)
if args.json:
print(json.dumps(results, indent=2))
else:
print(format_text(results))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Headcount Planner - Model hiring plans with cost, timeline, and business cases.
Generates hiring plans with fully-loaded cost projections, ramp models,
and ROI justification per role. Produces board-ready headcount proposals.
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
def plan_headcount(data: dict) -> dict:
"""Generate headcount plan with financial projections."""
roles = data.get("planned_hires", [])
current_headcount = data.get("current_headcount", 0)
current_arr = data.get("current_arr", 0)
budget_quarterly = data.get("hiring_budget_quarterly", 0)
results = {
"timestamp": datetime.now().isoformat(),
"current_state": {"headcount": current_headcount, "arr": current_arr, "arr_per_employee": round(current_arr / current_headcount) if current_headcount > 0 else 0},
"planned_hires": [],
"financial_impact": {},
"timeline": [],
"budget_analysis": {},
"recommendations": [],
}
total_annual_cost = 0
total_hires = 0
quarterly_costs = {1: 0, 2: 0, 3: 0, 4: 0}
for role in roles:
title = role.get("title", "")
department = role.get("department", "")
level = role.get("level", "L3")
base_salary = role.get("base_salary", 120000)
equity_value = role.get("equity_annual", 20000)
benefits_pct = role.get("benefits_pct", 25)
start_quarter = role.get("target_quarter", 1)
ramp_months = role.get("ramp_months", 3)
revenue_impact = role.get("expected_revenue_impact", 0)
risk_description = role.get("risk_if_not_filled", "")
business_case = role.get("business_case", "")
manager = role.get("reporting_to", "")
# Fully loaded cost
benefits = base_salary * (benefits_pct / 100)
tools_overhead = 12000 # annual estimate
fully_loaded = base_salary + benefits + equity_value + tools_overhead
monthly_cost = fully_loaded / 12
# Quarterly cost based on start quarter
for q in range(start_quarter, 5):
months_in_q = 3 if q > start_quarter else max(1, 3 - (start_quarter - 1))
quarterly_costs[q] += monthly_cost * months_in_q
# ROI calculation
roi = ((revenue_impact - fully_loaded) / fully_loaded) if fully_loaded > 0 and revenue_impact > 0 else 0
hire = {
"title": title,
"department": department,
"level": level,
"reporting_to": manager,
"target_quarter": f"Q{start_quarter}",
"base_salary": base_salary,
"fully_loaded_annual": round(fully_loaded),
"monthly_cost": round(monthly_cost),
"ramp_months": ramp_months,
"full_productivity_month": ramp_months,
"expected_revenue_impact": revenue_impact,
"roi": round(roi, 2),
"business_case": business_case,
"risk_if_not_filled": risk_description,
"priority": "Critical" if roi > 2 or "critical" in risk_description.lower() else "High" if roi > 1 else "Medium" if roi > 0 else "Support",
}
results["planned_hires"].append(hire)
total_annual_cost += fully_loaded
total_hires += 1
# Sort by priority
priority_order = {"Critical": 0, "High": 1, "Medium": 2, "Support": 3}
results["planned_hires"].sort(key=lambda x: priority_order.get(x["priority"], 4))
# Financial impact
new_arr_per_emp = current_arr / (current_headcount + total_hires) if (current_headcount + total_hires) > 0 else 0
results["financial_impact"] = {
"total_new_hires": total_hires,
"total_annual_cost": round(total_annual_cost),
"quarterly_cost_breakdown": {f"Q{q}": round(c) for q, c in quarterly_costs.items()},
"new_headcount": current_headcount + total_hires,
"new_arr_per_employee": round(new_arr_per_emp),
"arr_per_emp_change": round(new_arr_per_emp - results["current_state"]["arr_per_employee"]),
"total_revenue_impact": sum(h["expected_revenue_impact"] for h in results["planned_hires"]),
"plan_roi": round(sum(h["expected_revenue_impact"] for h in results["planned_hires"]) / total_annual_cost, 2) if total_annual_cost > 0 else 0,
}
# Timeline
for q in range(1, 5):
q_hires = [h for h in results["planned_hires"] if h["target_quarter"] == f"Q{q}"]
if q_hires:
results["timeline"].append({
"quarter": f"Q{q}",
"hires": len(q_hires),
"roles": [h["title"] for h in q_hires],
"quarterly_cost": quarterly_costs[q],
})
# Budget analysis
if budget_quarterly > 0:
over_budget_quarters = [f"Q{q}" for q, c in quarterly_costs.items() if c > budget_quarterly]
results["budget_analysis"] = {
"quarterly_budget": budget_quarterly,
"over_budget_quarters": over_budget_quarters,
"total_budget_annual": budget_quarterly * 4,
"total_planned_cost": round(total_annual_cost),
"within_budget": total_annual_cost <= budget_quarterly * 4,
}
# Recommendations
critical = [h for h in results["planned_hires"] if h["priority"] == "Critical"]
if critical:
results["recommendations"].append(f"{len(critical)} critical hire(s). Prioritize these regardless of other sequencing.")
fi = results["financial_impact"]
if fi["arr_per_emp_change"] < -10000:
results["recommendations"].append(f"ARR per employee decreases by ${abs(fi['arr_per_emp_change']):,.0f}. Ensure revenue hires compensate.")
return results
def format_text(results: dict) -> str:
"""Format as human-readable report."""
cs = results["current_state"]
fi = results["financial_impact"]
lines = [
"=" * 75,
"HEADCOUNT PLAN",
"=" * 75,
f"Current: {cs['headcount']} employees | ARR: ${cs['arr']:,.0f} | ARR/emp: ${cs['arr_per_employee']:,.0f}",
f"Planned: +{fi['total_new_hires']} hires | Cost: ${fi['total_annual_cost']:,.0f}/yr | Plan ROI: {fi['plan_roi']:.1f}x",
"",
f"{'Title':<22} {'Dept':<12} {'Q':>3} {'Loaded $':>10} {'Rev Impact':>11} {'ROI':>5} {'Priority':<10}",
"-" * 75,
]
for h in results["planned_hires"]:
lines.append(
f"{h['title']:<22} {h['department']:<12} {h['target_quarter']:>3} "
f"${h['fully_loaded_annual']:>9,.0f} ${h['expected_revenue_impact']:>10,.0f} "
f"{h['roi']:>4.1f}x {h['priority']:<10}"
)
lines.extend(["", "QUARTERLY COST:"])
for q, cost in fi["quarterly_cost_breakdown"].items():
lines.append(f" {q}: ${cost:,.0f}")
if results["budget_analysis"]:
ba = results["budget_analysis"]
status = "WITHIN BUDGET" if ba["within_budget"] else "OVER BUDGET"
lines.extend(["", f"BUDGET: {status} (${ba['total_planned_cost']:,.0f} vs ${ba['total_budget_annual']:,.0f} annual)"])
if results["recommendations"]:
lines.extend(["", "RECOMMENDATIONS:"])
for r in results["recommendations"]:
lines.append(f" -> {r}")
lines.extend(["", "=" * 75])
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Generate headcount plan with financial projections")
parser.add_argument("--input", "-i", help="JSON file with hiring plan data")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
if args.input:
with open(args.input) as f:
data = json.load(f)
else:
data = {
"current_headcount": 35,
"current_arr": 3000000,
"hiring_budget_quarterly": 200000,
"planned_hires": [
{"title": "Sr Backend Engineer", "department": "Engineering", "level": "L4", "base_salary": 180000, "equity_annual": 30000, "target_quarter": 1, "ramp_months": 3, "expected_revenue_impact": 0, "risk_if_not_filled": "Critical path blocked on API redesign", "reporting_to": "CTO", "business_case": "Unblock API platform rebuild"},
{"title": "Account Executive", "department": "Sales", "level": "L3", "base_salary": 140000, "equity_annual": 15000, "target_quarter": 1, "ramp_months": 4, "expected_revenue_impact": 500000, "risk_if_not_filled": "Pipeline uncovered in mid-market", "reporting_to": "CRO", "business_case": "Cover $2M pipeline gap"},
{"title": "Product Manager", "department": "Product", "level": "L3", "base_salary": 160000, "equity_annual": 25000, "target_quarter": 2, "ramp_months": 3, "expected_revenue_impact": 300000, "risk_if_not_filled": "Enterprise features delayed", "reporting_to": "CPO", "business_case": "Own enterprise product line"},
{"title": "SDR", "department": "Sales", "level": "L2", "base_salary": 65000, "equity_annual": 5000, "target_quarter": 1, "ramp_months": 2, "expected_revenue_impact": 200000, "risk_if_not_filled": "Pipeline generation below target", "reporting_to": "CRO", "business_case": "Generate 50 SQLs/quarter"},
{"title": "People Partner", "department": "People", "level": "L3", "base_salary": 120000, "equity_annual": 15000, "target_quarter": 2, "ramp_months": 2, "expected_revenue_impact": 0, "risk_if_not_filled": "HR ratio at 1:35, below minimum", "reporting_to": "CHRO", "business_case": "Support scaling from 35 to 50"},
],
}
results = plan_headcount(data)
if args.json:
print(json.dumps(results, indent=2))
else:
print(format_text(results))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Retention Risk Scorer - Assess employee retention risk and generate interventions.
Scores individuals across compensation, manager relationship, career growth,
engagement, tenure, and market demand. Generates prioritized intervention plans.
"""
import argparse
import json
import sys
from datetime import datetime
RISK_FACTORS = {
"comp_competitiveness": {"weight": 0.20, "scores": {1: "Above P50", 2: "At P50", 3: "Below P50"}},
"manager_relationship": {"weight": 0.20, "scores": {1: "Strong trust", 2: "Adequate", 3: "Friction or distrust"}},
"career_growth": {"weight": 0.20, "scores": {1: "Clear path, progressing", 2: "Path exists, slow", 3: "No visible path"}},
"engagement": {"weight": 0.15, "scores": {1: "High eNPS, advocates", 2: "Neutral", 3: "Disengaged"}},
"tenure_risk": {"weight": 0.10, "scores": {1: "< 1yr or > 3yr", 2: "1-2 years", 3: "18-24 months (cliff)"}},
"external_demand": {"weight": 0.15, "scores": {1: "Low market demand", 2: "Moderate", 3: "Hot market, recruiters active"}},
}
INTERVENTIONS = {
"low": ["Standard: competitive comp, regular 1:1s, career conversations", "Timeline: monitor quarterly"],
"medium": ["Proactive: skip-level conversation, comp review, stretch project", "Timeline: act within 30 days"],
"high": ["Urgent: retention package (comp + equity + role change), CEO involvement", "Timeline: act within 7 days"],
}
def score_retention(data: dict) -> dict:
"""Score retention risk for employees."""
employees = data.get("employees", [])
results = {
"timestamp": datetime.now().isoformat(),
"total_assessed": len(employees),
"risk_distribution": {"low": 0, "medium": 0, "high": 0},
"employee_scores": [],
"high_risk_list": [],
"intervention_plan": [],
"org_level_risks": [],
"recommendations": [],
}
manager_issues = {}
dept_risks = {}
for emp in employees:
name = emp.get("name", "Employee")
role = emp.get("role", "")
department = emp.get("department", "")
manager = emp.get("manager", "")
is_key_person = emp.get("key_person", False)
total_score = 0
factor_details = {}
for factor, config in RISK_FACTORS.items():
score = emp.get(factor, 2) # Default medium
score = min(3, max(1, score))
weighted = score * config["weight"]
total_score += weighted
factor_details[factor] = {"score": score, "label": config["scores"].get(score, ""), "weighted": round(weighted, 2)}
# Normalize to 6-18 range, then categorize
raw_total = sum(emp.get(f, 2) for f in RISK_FACTORS)
if raw_total <= 8:
risk_level = "low"
elif raw_total <= 13:
risk_level = "medium"
else:
risk_level = "high"
emp_result = {
"name": name,
"role": role,
"department": department,
"manager": manager,
"key_person": is_key_person,
"raw_score": raw_total,
"risk_level": risk_level,
"factors": factor_details,
"top_risk_factor": max(factor_details.items(), key=lambda x: x[1]["score"])[0],
}
results["employee_scores"].append(emp_result)
results["risk_distribution"][risk_level] += 1
if risk_level == "high":
results["high_risk_list"].append(emp_result)
urgency = "IMMEDIATE" if is_key_person else "This week"
results["intervention_plan"].append({
"employee": name,
"role": role,
"risk_score": raw_total,
"urgency": urgency,
"top_factor": emp_result["top_risk_factor"],
"interventions": INTERVENTIONS["high"],
})
elif risk_level == "medium":
results["intervention_plan"].append({
"employee": name,
"role": role,
"risk_score": raw_total,
"urgency": "Within 30 days",
"top_factor": emp_result["top_risk_factor"],
"interventions": INTERVENTIONS["medium"],
})
# Track manager-level patterns
if emp.get("manager_relationship", 2) == 3:
manager_issues[manager] = manager_issues.get(manager, 0) + 1
# Track department risks
if department not in dept_risks:
dept_risks[department] = {"total": 0, "high": 0}
dept_risks[department]["total"] += 1
if risk_level == "high":
dept_risks[department]["high"] += 1
# Sort intervention plan by urgency
urgency_order = {"IMMEDIATE": 0, "This week": 1, "Within 30 days": 2}
results["intervention_plan"].sort(key=lambda x: urgency_order.get(x["urgency"], 3))
# Org-level risks
for manager, count in manager_issues.items():
if count >= 2:
results["org_level_risks"].append({
"type": "Manager issue",
"detail": f"{manager} has {count} reports with manager relationship friction",
"action": "Investigate manager effectiveness. This is a manager problem, not a culture problem.",
})
for dept, stats in dept_risks.items():
if stats["high"] >= 2:
results["org_level_risks"].append({
"type": "Department risk",
"detail": f"{dept} has {stats['high']}/{stats['total']} employees at high retention risk",
"action": "Department-wide retention review needed.",
})
# Recommendations
high_count = results["risk_distribution"]["high"]
total = results["total_assessed"]
high_pct = (high_count / total * 100) if total > 0 else 0
if high_pct > 15:
results["recommendations"].append(f"URGENT: {high_pct:.0f}% of assessed employees are high risk. Structural intervention needed.")
if high_count > 0:
key_persons_at_risk = [e for e in results["high_risk_list"] if e["key_person"]]
if key_persons_at_risk:
results["recommendations"].append(f"{len(key_persons_at_risk)} key person(s) at high risk. CEO involvement required.")
if manager_issues:
results["recommendations"].append(f"{len(manager_issues)} manager(s) flagged for relationship issues across multiple reports.")
return results
def format_text(results: dict) -> str:
"""Format as human-readable report."""
rd = results["risk_distribution"]
lines = [
"=" * 70,
"RETENTION RISK ASSESSMENT",
"=" * 70,
f"Date: {results['timestamp'][:10]} | Employees Assessed: {results['total_assessed']}",
f"Distribution: {rd['low']} Low | {rd['medium']} Medium | {rd['high']} High",
"",
f"{'Name':<20} {'Role':<18} {'Dept':<12} {'Score':>6} {'Risk':<8} {'Top Factor'}",
"-" * 70,
]
for e in sorted(results["employee_scores"], key=lambda x: x["raw_score"], reverse=True):
icon = "[R]" if e["risk_level"] == "high" else "[Y]" if e["risk_level"] == "medium" else "[G]"
key = " *" if e["key_person"] else ""
lines.append(
f"{e['name']:<20} {e['role']:<18} {e['department']:<12} {e['raw_score']:>5}/18 "
f"{icon} {e['risk_level']:<5}{key} {e['top_risk_factor']}"
)
if results["intervention_plan"]:
lines.extend(["", "INTERVENTION PLAN:"])
for ip in results["intervention_plan"][:8]:
lines.append(f" [{ip['urgency']}] {ip['employee']} ({ip['role']})")
lines.append(f" Factor: {ip['top_factor']} | {ip['interventions'][0]}")
if results["org_level_risks"]:
lines.extend(["", "ORG-LEVEL RISKS:"])
for r in results["org_level_risks"]:
lines.append(f" [{r['type']}] {r['detail']}")
lines.append(f" Action: {r['action']}")
if results["recommendations"]:
lines.extend(["", "RECOMMENDATIONS:"])
for r in results["recommendations"]:
lines.append(f" -> {r}")
lines.extend(["", "* = key person", "=" * 70])
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Score employee retention risk")
parser.add_argument("--input", "-i", help="JSON file with employee data")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
if args.input:
with open(args.input) as f:
data = json.load(f)
else:
data = {"employees": [
{"name": "Alice Chen", "role": "Sr Engineer", "department": "Engineering", "manager": "Bob", "key_person": True, "comp_competitiveness": 3, "manager_relationship": 2, "career_growth": 3, "engagement": 2, "tenure_risk": 3, "external_demand": 3},
{"name": "David Kim", "role": "Product Manager", "department": "Product", "manager": "Sarah", "key_person": True, "comp_competitiveness": 2, "manager_relationship": 1, "career_growth": 2, "engagement": 1, "tenure_risk": 1, "external_demand": 2},
{"name": "Emma Wilson", "role": "SDR Lead", "department": "Sales", "manager": "Tom", "key_person": False, "comp_competitiveness": 2, "manager_relationship": 3, "career_growth": 2, "engagement": 3, "tenure_risk": 3, "external_demand": 2},
{"name": "Frank Lopez", "role": "DevOps", "department": "Engineering", "manager": "Bob", "key_person": False, "comp_competitiveness": 3, "manager_relationship": 3, "career_growth": 2, "engagement": 2, "tenure_risk": 2, "external_demand": 3},
{"name": "Grace Park", "role": "Marketing Dir", "department": "Marketing", "manager": "CEO", "key_person": True, "comp_competitiveness": 1, "manager_relationship": 1, "career_growth": 1, "engagement": 1, "tenure_risk": 1, "external_demand": 2},
]}
results = score_retention(data)
if args.json:
print(json.dumps(results, indent=2))
else:
print(format_text(results))
if __name__ == "__main__":
main()
Related skills
How it compares
Pick chro-advisor when you need structured people-ops frameworks and Python-modeled headcount or comp analysis rather than generic HR policy templates.
FAQ
What Python tools does chro-advisor include?
chro-advisor ships three stdlib Python scripts: retention_risk_scorer.py scores six-factor attrition risk, headcount_planner.py models fully-loaded hiring costs and ramp timelines, and comp_band_analyzer.py audits compa-ratios and pay equity flags. Each accepts JSON input and sup
When should a developer invoke chro-advisor?
chro-advisor activates when building hiring plans, designing compensation frameworks, restructuring teams, managing performance reviews, or addressing retention spikes. Trigger keywords include headcount planning, salary bands, org design, eNPS, and regrettable attrition.
What deliverables does chro-advisor produce?
chro-advisor outputs headcount plans with per-role business cases, compensation band frameworks with equity guidelines, org chart proposals with span targets, retention analyses with intervention ladders, and board-ready people KPI slides with measurable success criteria.