
Program Manager
- 233 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Coordinate multi-workstream programs during active delivery: milestones, dependencies, RAID logs, status reporting, and cross-team execution tracking.
About
program-manager equips Claude to act as a program manager during build: maintain roadmaps and RAID logs, track milestones across teams, draft exec-ready status updates, and resolve dependency conflicts so large initiatives ship with visible governance.
- Multi-workstream coordination
- Milestone and dependency tracking
- RAID and status reporting
- Stakeholder communication cadence
- Delivery risk escalation
Program Manager by the numbers
- 233 all-time installs (skills.sh)
- Ranked #984 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 program-managerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 233 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Coordinate multi-workstream programs during active delivery: milestones, dependencies, RAID logs, status reporting, and cross-team execution tracking.
Files
Program Manager
The agent acts as an expert program manager coordinating complex multi-project initiatives. It structures governance, manages cross-project dependencies, tracks benefits realization, and communicates status to steering committees with appropriate escalation.
Core Capabilities
- Program structure & governance — portfolio→program→project→workstream hierarchy, governance bodies, decision rights, escalation matrix.
- Charter creation — business case, scope, structure, governance, and benefit-linked success criteria with required sponsor sign-off.
- Dependency & critical-path mapping — cross-project dependency matrix, integration tracking, mitigation for high-risk links.
- Resource & benefits planning — FTE/budget forecasting with over-allocation flags; benefits baselined at program start and tracked to target.
- Status reporting & stakeholder management — RAG dashboards per governance body, Mendelow power-interest segmentation, risk register scoring.
When to Use
- Standing up a new program (structure, governance, charter, sponsor).
- Managing cross-project dependencies and a shared critical path.
- Tracking benefits realization against the original business case.
- Reporting program status to steering committees and managing escalations.
Clarify First
Before generating the program artifact, confirm these inputs. If any is unknown or vague, ASK — do not assume:
- [ ] Which artifact — program charter, dependency/critical-path map, benefits tracker, or steering status report (each has a distinct structure)
- [ ] Audience / governance body — steering committee, sponsor, or working team (sets the RAG altitude and escalation framing)
- [ ] Project and dependency data — the projects, cross-project links, and milestones in scope (the critical path and matrix are only as good as this)
- [ ] Benefits baseline — the original business-case targets, when tracking benefits (realization is meaningless without the baseline)
Stop rule: ask only the 2-3 that most change the output. If the user says "just draft it," proceed and list your assumptions at the top of the artifact.
Quick Start
python scripts/dependency_analyzer.py --projects projects.yaml # critical path
python scripts/resource_forecast.py --program program.yaml --months 12
python scripts/benefits_tracker.py --plan benefits_plan.yaml
python scripts/program_dashboard.py --program "Name" # RAG dashboardRun the six-step workflow (structure → charter → dependencies → resources → benefits → status) — full procedure, tables, validation checkpoints, and dashboard format are in the operating guide below.
References
references/program-operating-guide.md— read this when running the program: six-step workflow, governance/escalation tables, dependency matrix, dashboard example, stakeholder & risk management, tool commands, troubleshooting, and success criteria.references/red-flags.md— read this before publishing any program artifact (dependency map, RAID log, steering deck, status report): common failure modes with bad/good examples and fixes.
Scope & Limitations
In Scope: Program charter creation, governance structure design, cross-project dependency management, benefits realization tracking, resource allocation planning, stakeholder communication, risk management, milestone tracking, steering committee facilitation, escalation management.
Out of Scope: Individual project execution (hand off to project managers), sprint-level delivery (hand off to scrum-master/), tool configuration (hand off to jira-expert/), production deployments (hand off to delivery-manager/), budget approval authority (retained by Steering Committee).
Limitations: Benefits realization accuracy depends on finance team providing baseline and actual financial data. Resource forecasting assumes stable team composition -- high attrition invalidates projections. Governance effectiveness requires consistent executive participation; sponsor turnover can reset program momentum. SAFe/LeSS scaling recommendations assume teams have achieved at least agile maturity Level 2.
Integration Points
| Integration | Direction | What Flows |
|---|---|---|
senior-pm/ | Bidirectional | Portfolio priorities inform program scope; program status feeds portfolio dashboard |
delivery-manager/ | PgM -> DM | Program milestones and release windows; cross-project deployment coordination |
agile-coach/ | Coach -> PgM | Scaling framework recommendations (SAFe, LeSS) inform program governance design |
scrum-master/ | SM -> PgM | Team velocity and capacity data for resource forecasting |
jira-expert/ | PgM -> Jira | Cross-project epic tracking, program-level dashboards, dependency issue types |
confluence-expert/ | PgM -> Confluence | Program charter, governance docs, stakeholder communication archives |
Example: Multi-Project Portfolio Review for a GA Launch
Real-world scenario showing how to apply this skill end-to-end.
Context
Northwind SaaS (fintech, Series C, 200 people) is coordinating five teams toward a single GA launch of "Multi-Entity Treasury" on 2026-12-04 -- a contracted commit to flagship customer Skyway Logistics. The Program Manager (Petra) was hired six months ago specifically to run cross-team programs after Northwind's Q3 GA missed by 5 weeks because three dependencies were caught too late.
The five contributing teams are: Treasury Core, Compliance, Platform, Mobile, and Activation. Each has its own squad lead, sprint cadence, and tracker. Petra runs a portfolio review every two weeks plus a steering committee monthly. This artifact is the bi-weekly review for May 22, 2026 -- T-minus 27 weeks to GA.
Inputs
- 5 teams, 5 squad leads, 1 sponsor (CTO Hari)
- 1 GA deadline: 2026-12-04 (contracted with Skyway)
- 47 work items across 5 teams, 18 of which have cross-team dependencies
- Risk register: 9 active risks; 2 escalated to steering
- Budget: not a constraint (capacity is)
Applying the skill
1. Aggregate status across all 5 teams in one view (R/Y/G + headline). 2. Map dependencies and compute the critical path. 3. Triage risks by Likelihood x Impact (Expected Monetary Value where applicable). 4. Prepare escalations with named owners and decisions needed. 5. Issue a single program-status artifact to the steering committee and the five teams.
The artifact
---
Multi-Entity Treasury: Program Status -- 2026-05-22
Program manager: Petra Sandoval Sponsor: Hari Mukherjee (CTO) GA target: 2026-12-04 (T-27 weeks) Overall status: YELLOW Status rationale: Critical path is intact but two cross-team dependencies are aging into risk; compliance audit evidence collection is behind plan.
Team-level status snapshot
| Team | Status | Confidence | Lead | Headline |
|---|---|---|---|---|
| Treasury Core | Yellow | 0.7 | Tomas Veliz | FX edge cases discovered; 2 weeks behind on Multi-Entity API |
| Compliance | Yellow | 0.65 | Hari Patel | SOC 2 Type II evidence at 65% (plan was 75% by today) |
| Platform | Green | 0.85 | Marcus Vella | Audit-log infra on schedule; ready for Compliance to use |
| Mobile | Green | 0.9 | Jin Lee | Multi-entity sign-in flow in design review; no concerns |
| Activation | Green | 0.85 | Ramon Cortes | Multi-entity onboarding tour spec done; build starts next week |
Critical path
gantt
title Multi-Entity Treasury -- Critical Path
dateFormat YYYY-MM-DD
section Treasury Core
Data model migration :done, dm, 2026-04-01, 2026-05-15
Multi-entity API :active, api, after dm, 60d
Permission layer :perm, after api, 21d
section Compliance
SOC 2 evidence collection :active, soc2, 2026-04-01, 120d
Audit log integration :audit, after soc2, 14d
section Platform
Audit-log infra :done, alinfra, 2026-04-15, 2026-05-20
Region failover for ME :rf, 2026-09-01, 21d
section Mobile
ME sign-in flow :msi, 2026-06-01, 30d
section Activation
ME onboarding tour :tour, 2026-06-15, 28d
section Integration
End-to-end QA :qa, 2026-10-15, 30d
Skyway pilot :sk, 2026-11-15, 14d
GA :milestone, ga, 2026-12-04, 0dCritical path runs: Data model migration -> Multi-entity API -> Permission layer -> E2E QA -> Skyway pilot -> GA.
Current slack on critical path: 3 weeks (was 5 weeks before the FX edge-case discovery).
Cross-team dependencies (the 18)
| # | From | To | What | Status | Due | At risk? |
|---|---|---|---|---|---|---|
| 1 | Platform | Treasury Core | Audit-log API ready | Done | -- | No |
| 2 | Platform | Compliance | Audit-log tamper evidence | In progress | 2026-06-15 | No |
| 3 | Treasury Core | Mobile | ME API contract finalized | Slipping | 2026-06-01 | YES (1 week late) |
| 4 | Treasury Core | Activation | ME entity-creation API | Slipping | 2026-06-15 | YES (1 week late) |
| 5 | Compliance | Treasury Core | Audit field requirements doc | Pending | 2026-05-29 | At risk |
| 6 | Mobile | Treasury Core | Permission token format | Done | -- | No |
| 7-18 | (various lower-priority) | Mostly on track |
Top concern: Dependencies 3 and 4 are slipping. Mobile and Activation cannot start their cross-entity work until Treasury Core's API contract is final. If it slips two more weeks, both teams lose July build time.
Risk register (active)
| # | Risk | L | I | EMV* | Mitigation | Owner | Due | Status |
|---|---|---|---|---|---|---|---|---|
| R1 | Treasury Core API delay cascades to Mobile + Activation | H | H | $480K | Daily standup, scope freeze on API, contractor for ramp | Tomas Veliz | 2026-06-01 | Escalating |
| R2 | SOC 2 evidence behind plan -> Skyway pilot can't sign | M | H | $240K | Compliance daily standup, weekly Infra coord call | Hari Patel | 2026-07-15 | Active |
| R3 | Skyway requirements change after pilot start | M | H | $300K | Weekly Skyway sync, written change-control | Mira Chen (VP Sales) | Ongoing | Mitigated |
| R4 | Multi-entity FX rate provider has rate limits | M | M | $40K | Engage provider for higher tier | Tomas Veliz | 2026-06-15 | Active |
| R5 | Activation rate could miss OKR if ME onboarding flow is unfamiliar | M | M | -- | A/B test ME flow vs single-entity | Ramon Cortes | 2026-08-15 | Active |
| R6 | Designers shared with another program | L | M | -- | Lock designer time in PM | Ines Petrov | 2026-06-01 | Mitigated |
| R7 | Skyway expects features not in scope | M | M | $120K | Written scope doc countersigned | Mira + Hari M. | 2026-06-15 | Active |
| R8 | Mobile App-Store approval slips | L | H | $360K | Submit 3 weeks early | Jin Lee | 2026-11-01 | Watch |
| R9 | Annual security audit overlaps Compliance team Q4 | M | M | -- | Push security audit to Q1 2027 if possible | Hari Patel | 2026-06-15 | Active |
*EMV (Expected Monetary Value) is Likelihood-weighted impact in ARR-equivalent terms.
Decisions needed from steering committee
Decision 1: Bring in a contractor for Treasury Core?
Context: R1 is the single biggest risk to GA. Tomas estimates a 4-week contractor on ME API work would buy back 2 weeks of slack. Cost: $42K for an 8-week contractor engagement Recommendation: Approve. EMV reduction far exceeds the cost. Required by: 2026-05-29
Decision 2: Freeze ME API scope on 2026-06-01?
Context: Mobile and Activation are blocked until the API contract is final. Tomas wants to keep iterating; the dependency teams need a final. Recommendation: Freeze on 2026-06-01. Any post-freeze change requires PM-level approval. Required by: 2026-05-29
Decision 3: Skyway pilot start -- hold or slip?
Context: Pilot was Nov 1 start. SOC 2 evidence pace makes Nov 1 marginal. Recommendation: Hold Nov 1 with a Yellow flag. Decide at 2026-09 steering whether to slip 2 weeks. Required by: 2026-09 steering
Status by deliverable
Multi-Entity API (Treasury Core)
- Scope: 12 endpoints + permission layer + audit hooks
- Completion: 65% (was 80% projected by today)
- Blocking: dependencies 3 and 4
- Mitigation: contractor (Decision 1), scope freeze (Decision 2)
SOC 2 Type II Evidence Collection (Compliance)
- Scope: 87 control evidence items
- Completion: 65% (was 75% projected)
- Blocking: Infra team availability (R9)
- Mitigation: daily standup; if not >=80% by 2026-06-15, escalate to CTO
Audit Log Infrastructure (Platform)
- Scope: tamper-evident append-only log + query API
- Completion: 100%
- Blocking: none
- Status: handed to Compliance for SOC 2 use
Multi-Entity Mobile Sign-In (Mobile)
- Scope: switch entity from sign-in screen
- Completion: 40% (in design)
- Blocking: dependency 3 (API contract)
- Status: design proceeding on placeholder contract; rework risk Y if dep 3 changes shape
Multi-Entity Onboarding Tour (Activation)
- Scope: in-app tour for users switching contexts
- Completion: 10% (spec done; build pending)
- Blocking: dependency 4 (entity-creation API)
- Status: build can start with placeholder API; rework risk same as Mobile
Resource allocation snapshot
| Team | Engineers on this program | Engineers on other work | Risk |
|---|---|---|---|
| Treasury Core | 8 | 6 | Will pull 2 more from other work if R1 worsens |
| Compliance | 7 | 2 | Stable |
| Platform | 4 | 14 | Stable; 1 engineer rotates back after audit-log handoff |
| Mobile | 5 | 0 | Stable |
| Activation | 4 | 8 | Stable; can pull 1 more if needed |
What changed since last review (2026-05-08)
- Treasury Core slipped from Green to Yellow (FX edge cases)
- Compliance moved from Green to Yellow (SOC 2 pace)
- Platform completed audit-log infra (on schedule)
- Two new risks added (R4 FX provider, R7 Skyway scope expansion)
- Dependency 3 + 4 moved to "Slipping" status
What I will report to the steering committee (Jun 5)
- Overall Yellow with named mitigations
- Three decisions needed (contractor, scope freeze, Skyway hold/slip)
- R1 is the dominant risk; everything else is manageable
- Critical-path slack remains at 3 weeks if Decisions 1 + 2 land
Why this works
- One snapshot covers five teams. The steering committee does not have to read five separate updates.
- Critical path is named and visualized. The 3-week slack number is the most-watched metric.
- Cross-team dependencies are listed with status, due dates, and at-risk flags. Slipping dependencies are surfaced before they cascade.
- Risks use Likelihood x Impact + EMV in dollar terms. Steering can compare risks against the contractor cost (Decision 1).
- Decisions needed are written with context + recommendation + required-by date. Steering can decide in the meeting instead of taking it offline.
- Resource allocation includes "engineers on other work" so the steering committee understands the slack each team has.
What's next
- Issue this artifact to the steering committee and five team leads (2026-05-23).
- Schedule a 30-minute decision-only steering session on 2026-05-29 for Decisions 1 + 2.
- Update the dependency tracker via
../execution/dependency-map/after each team standup. - Roll team-level risks into the portfolio register via
../senior-pm/. - Use
../execution/status-update-generator/to feed individual team weekly updates into this program-level view. - Re-run this artifact every two weeks; archive prior versions for trend visibility.
Program Operating Guide
Read this when running the program end-to-end: the six-step workflow, dashboard format, stakeholder/risk management, tool commands, troubleshooting, and success criteria. Pull the section that matches your current step.
---
Workflow
1. Define Program Structure
The agent establishes the program hierarchy and governance:
PORTFOLIO (Strategic alignment, investment decisions, resource allocation)
-> PROGRAM (Benefit realization, cross-project coordination, governance)
-> PROJECTS (Deliverables, timeline, budget)
-> WORKSTREAMS (Tasks, activities, resources)Governance bodies:
| Body | Cadence | Authority |
|---|---|---|
| Steering Committee | Monthly | Strategic decisions, escalations, funding |
| Program Board | Bi-weekly | Governance, progress review, issue resolution |
| Project Sync | Weekly | Coordination, dependency management |
Decision rights:
- Budget changes >$X: Steering Committee
- Scope changes: Program Board
- Schedule changes <2 weeks: Program Manager
Validation checkpoint: Every program must have a named Executive Sponsor, defined decision rights, and an escalation matrix before proceeding.
2. Create Program Charter
The agent drafts a charter covering:
1. Executive Summary -- One paragraph describing the program 2. Business Case -- Problem statement, strategic alignment, expected benefits with measurable targets, investment (budget, duration, FTE), ROI analysis (NPV, IRR, payback period) 3. Scope -- In/out of scope, assumptions, constraints 4. Program Structure -- Projects table (description, owner, duration), dependency map 5. Governance -- Steering committee members, decision rights, meeting cadence 6. Success Criteria -- Measurable targets tied to benefits
Validation checkpoint: Charter requires sign-off from Sponsor and Business Owner before project kickoff.
3. Map Dependencies
The agent analyzes cross-project dependencies and identifies the critical path:
python scripts/dependency_analyzer.py --projects projects.yamlDependency matrix example:
Project A Project B Project C Project D
Project A - -> ->
Project B <- - ->
Project C <- - ->
Project D <- <- -
Critical Path: A (Design) -> B (API) -> C (Integration) -> D (Launch)Integration points to track:
| Integration | Projects | Interface | Owner | Risk Level |
|---|---|---|---|---|
| API Contract | A -> B | REST API | Team B | Medium |
| Data Migration | B -> C | ETL Pipeline | Team C | High |
| SSO Integration | A, B, C | SAML | Team A | Low |
Validation checkpoint: Any High-risk dependency must have a mitigation plan and a named owner before the dependent project starts.
4. Plan Resources
The agent creates a resource allocation plan:
python scripts/resource_forecast.py --program program.yaml --months 12Output includes per-role monthly allocation, total FTE forecast, and budget per month. The agent flags resource conflicts where a person is allocated >100% across projects.
5. Track Benefits Realization
python scripts/benefits_tracker.py --plan benefits_plan.yamlFor each benefit, the agent tracks:
- Definition: Metric, baseline, target
- Measurement: Data source, frequency, owner
- Realization timeline: Quarterly targets vs. actuals with variance
Validation checkpoint: Benefits tracking begins at program start, not after delivery. Early measurement of leading indicators confirms the program is on track to deliver value.
6. Report Status
The agent generates program status reports for each governance body:
Dashboard structure:
PROGRAM STATUS: [Name] Overall: GREEN/AMBER/RED
Schedule: [status] [trend] Budget: [status] [%used]
Scope: [status] [trend] Quality: [status]
Risk: [count] High risks Resources: [status]
PROJECT STATUS:
Project A: [status] [% complete] [next milestone]
Project B: [status] [% complete] [next milestone]
KEY METRICS:
Milestones: X/Y completed Benefits: $Xm realized
Issues: X open (Y critical) Deliverables: X/Y completeEscalation matrix:
| Level | Criteria | Escalate To | Response Time |
|---|---|---|---|
| 1 | Team issue | Project Manager | 24 hours |
| 2 | Project impact | Program Manager | 48 hours |
| 3 | Program impact | Program Board | 1 week |
| 4 | Strategic impact | Steering Committee | 2 weeks |
Validation checkpoint: Any item at RED status for 2+ reporting periods must be escalated to the next governance level with a recovery plan.
Example: Program Dashboard Generation
$ python scripts/program_dashboard.py --program "Digital Transformation"
Program: Digital Transformation Status: AMBER (At Risk)
Sponsor: Jane Smith Phase: Execution
==========================================================
Schedule: AMBER (-2 weeks) Budget: GREEN (92% of plan)
Scope: GREEN (on track) Quality: GREEN (meets standards)
Risk: AMBER (3 High risks) Resources: GREEN (stable)
Project Status:
Project A: GREEN 100% complete (Complete)
Project B: AMBER 65% complete (Next: API Delivery Feb 10)
Project C: GREEN 40% complete (Next: Integration Start Feb 20)
Project D: BLUE 0% complete (Not Started)
Key Metrics:
Milestones: 4/8 (50%) Benefits: $1.2M realized / $10M target
Issues: 5 open (2 critical) Deliverables: 12/20 (60%)
Upcoming Milestones:
M5: Beta Release - Feb 15 (AMBER - at risk)
M6: UAT Complete - Mar 01 (GREEN - on track)Stakeholder Management
The agent maps stakeholders using Mendelow's Power-Interest Grid:
| Quadrant | Stakeholders | Strategy |
|---|---|---|
| High Power, High Interest | CEO, CTO, Business Owner | Manage closely -- regular 1:1s, involve in decisions |
| High Power, Low Interest | CFO, Legal | Keep satisfied -- executive summaries, escalate blockers |
| Low Power, High Interest | End Users, Teams | Keep informed -- newsletters, demos, feedback channels |
| Low Power, Low Interest | Vendors, Support | Monitor -- periodic updates as needed |
Communication plan:
| Audience | Content | Frequency | Channel |
|---|---|---|---|
| Steering Committee | Program status, decisions needed | Monthly | Meeting |
| Program Board | Detailed status, issue resolution | Bi-weekly | Meeting |
| Project Managers | Coordination, dependencies | Weekly | Meeting |
| Teams | Updates, context | Weekly | |
| End Users | Progress, upcoming changes | Monthly | Newsletter |
Risk Management
The agent maintains a program risk register scored by Probability x Impact (1-5 scale):
| Score Range | Classification | Action |
|---|---|---|
| 15-25 | Critical | Immediate mitigation, escalate to Steering Committee |
| 8-14 | High | Active mitigation plan, report to Program Board |
| 4-7 | Medium | Monitor, mitigation plan on standby |
| 1-3 | Low | Accept and monitor |
Tools
| Tool | Purpose | Command |
|---|---|---|
program_dashboard.py | Generate program status dashboard | python scripts/program_dashboard.py --program "Name" |
dependency_analyzer.py | Analyze cross-project dependencies | python scripts/dependency_analyzer.py --projects projects.yaml |
benefits_tracker.py | Track benefits realization vs. plan | python scripts/benefits_tracker.py --plan benefits_plan.yaml |
resource_forecast.py | Forecast resource allocation | python scripts/resource_forecast.py --program program.yaml --months 12 |
Troubleshooting
| Problem | Likely Cause | Resolution |
|---|---|---|
| Cross-project dependencies cause cascading delays | Dependencies identified too late or dependency owners not empowered to resolve conflicts | Run dependency mapping at program kickoff and refresh biweekly; assign a named owner to every high-risk dependency with authority to escalate |
| Benefits realization tracking shows zero progress months into execution | Benefits not baselined at program start, or measurement relies on lagging indicators only | Establish baselines before project kickoff; define leading indicators that show early directional progress (e.g., adoption rate before revenue impact) |
| Steering committee meetings devolve into status updates | No decision agenda; status information not distributed in advance | Send status dashboard 48 hours before meeting; structure agenda around decisions needed, risks requiring escalation, and resource requests only |
| Resource conflicts across projects are never resolved | No single view of resource allocation; project managers negotiate bilaterally | Maintain a centralized resource allocation dashboard; escalate conflicts above 100% allocation to the Program Board with options |
| Program status is always "green" until sudden "red" | Project managers fear escalation; status criteria are subjective | Define objective RAG thresholds (e.g., >5 days late = Amber, >15 days = Red); normalize escalation as a positive signal, not a failure |
| Governance overhead slows delivery | Too many approval gates, overlapping governance bodies, or unclear decision rights | Streamline to three governance tiers maximum; publish a RACI for every decision type; delegate routine decisions to lowest appropriate level |
| Stakeholders disengage from the program | Communication is generic, too frequent, or not relevant to their interests | Segment communication by Mendelow quadrant; tailor content to each audience's concerns; reduce frequency for low-interest stakeholders |
Success Criteria
- All cross-project dependencies are identified, owned, and tracked with biweekly status updates
- Benefits realization reaches at least 50% of target by the program midpoint (measured by leading indicators)
- Steering committee meetings result in documented decisions within 48 hours of the meeting
- Resource allocation conflicts are resolved within 5 business days of identification
- Program status reports are distributed on schedule with 100% cadence compliance
- No project remains at RED status for more than 2 consecutive reporting periods without an escalation and recovery plan
- Program closes with a formal benefits realization report comparing actuals to the original business case
Red Flags: Program Manager
Common ways this skill's output goes wrong -- concrete examples, why they're bad, and how to fix them.
How to use this document
Scan every program-level artifact (dependency map, RAID log, steering committee deck, cross-team status report) before publishing. Each red flag has bad and good quoted examples.
---
Red Flag 1: Dependency Theater
Symptom. Dependency map shows 47 dependencies; in the weekly sync, the PgM reads them aloud; no decisions are made. Why it's bad. A dependency map is a coordination tool, not a status report. Reading dependencies aloud without driving decisions (re-sequence, descope, escalate) makes the meeting feel productive but changes nothing. The dependencies still slip the program. Bad example:
"Weekly sync minutes: 'Reviewed all 47 dependencies. Team A waiting on Team B; Team C waiting on Team A.' (45-min meeting; no action items.)"
Good example:
"Weekly sync structure: (1) what changed in dependencies since last week (5 min); (2) which 2-3 dependencies are critical-path this week (10 min); (3) decisions / escalations needed (15 min). Output: an updated dependency map + a decision log. No dependency is read aloud unless it triggers an action."
How to catch it. Sync meeting > 30 min with no decisions in the log = dependency theater.
---
Red Flag 2: RAID Log Nobody Reads
Symptom. RAID log (Risks / Assumptions / Issues / Dependencies) has 240 entries. The last update was 3 weeks ago. Steering committee skips the RAID section. Why it's bad. A stale, oversized RAID log is worse than no RAID log. It signals risk hygiene exists but is broken, so people stop looking. Real risks are buried under noise. Bad example:
"RAID log: 240 entries. Most are 'open' indefinitely. No owner column. Steering committee skips."
Good example:
"RAID log capped at ~25 active entries (closed items archived monthly). Each entry has: Owner, Status, Mitigation, Tripwire, Next Review Date. Weekly review with the program team rotates ~5 entries; quarterly review with steering committee surfaces top-5 risks only."
How to catch it. RAID > 30 active entries or last review > 2 weeks ago = restructure.
---
Red Flag 3: Status Reporting Up Without Decisions
Symptom. Steering committee deck is 40 slides of status; ends with "any questions?"; no decisions asked of leadership. Why it's bad. A steering committee exists to make decisions. Status-only meetings waste leadership time and the PgM loses the chance to unblock the program. Decisions slip because they were never explicitly surfaced. Bad example:
"Steering committee deck: 40 slides on team status. Final slide: 'Questions?' No 'asks' section. No decisions made."
Good example:
"Steering committee deck: 8 slides max. Structure: (1) headline R/Y/G; (2) what changed since last meeting; (3) 3 explicit asks for the committee (e.g. 'approve $80k vendor switch', 'decide between scope option A or B', 'allocate engineer from Team C to Program X'); (4) appendix with detail. Meeting closes with decisions, not questions."
How to catch it. Steering deck > 15 slides or no Asks section = restructure.
---
Red Flag 4: Cross-Team Dependencies Discovered Mid-Program
Symptom. Week 12 of a 16-week program: Team A discovers Team B was supposed to deliver an API that Team B never planned. Why it's bad. Mid-program dependency discovery is the leading cause of program failure. By week 12 there is no slack to absorb the shock, and one team's slip cascades into 2-3 others. The program slips by months. Bad example:
"Week 12: Team A blocked. Team B: 'We never knew we owed you that API.' Estimated 4 weeks to deliver. Program slips 4 weeks."
Good example:
"Week 0: dependency reconciliation workshop with all program teams. Every dependency captured in dependency-graph.json with owner + due date. Weeks 1, 4, 8: dependency check-ins specifically. Discovered dependency added with retrospective lesson logged."How to catch it. Mid-program dependency surprise = run a week-0 reconciliation next time; see execution/dependency-map.
---
Red Flag 5: Critical-Path Tracking Lost After Kickoff
Symptom. Kickoff identified the critical path; week 8 nobody knows what is on it. Why it's bad. The critical path is the only sequence whose slip directly slips the program. Losing track of it means the PgM cannot prioritize attention. Non-critical items get equal worry; the critical items quietly slip. Bad example:
"Kickoff: critical path = task A -> task C -> task F. Week 8: PgM tracks 'all 22 tasks equally'. Task C slipped 1 week unnoticed."
Good example:
"Critical path recomputed weekly using dependency_graph.py --critical-path. Weekly status flags 'critical-path items at risk' first. Items off the critical path get less attention; items on it get daily check-ins if at risk."How to catch it. No critical-path computation in the last 2 weeks = re-run.
---
Red Flag 6: Risk Tripwires Missing
Symptom. Every risk in the RAID log has a description but no tripwire (the specific signal that means "we are now in trouble"). Why it's bad. Without tripwires, risks fire silently. By the time the PgM realizes risk R has materialized, mitigation is more expensive. Tripwires are the early-warning system; without them, the program is reactive only. Bad example:
"Risk: 'Vendor delivery may slip.' Mitigation: 'Stay in touch with vendor.' (No tripwire.)"
Good example:
"Risk: 'Vendor delivery may slip.' Tripwire: 'Vendor weekly status shows red OR Vendor misses a milestone OR Vendor's primary engineer changes.' Mitigation: 'If tripwire fires, switch to Plan B (in-house build with descoped functionality).' Owner: <name>. Review weekly."
How to catch it. Any risk without a tripwire column = add or move to issue.
---
Red Flag 7: Program Charter Was Never Written
Symptom. The program has been running for 6 months; no document defines its scope, success criteria, sponsor, or constraints. Why it's bad. Without a charter, the program drifts. New requests get accepted because there is no documented basis for refusal. Success at close is contested ("did we achieve the goal?") because the goal was never written down. Bad example:
"Program 'Enterprise Readiness' has been running since November. No charter. Scope reinterpreted in every steering meeting."
Good example:
"Program Charter (1-2 pages) covers: (1) goal + success metrics; (2) scope -- what is and is not in; (3) sponsor + steering committee; (4) participating teams; (5) budget + timeline; (6) decision rights (DACI); (7) explicit out-of-scope list. Signed by sponsor at kickoff."
How to catch it. Program > 4 weeks old without a written charter = pause and write.
---
Red Flag 8: Single PgM as Bottleneck for All Cross-Team Decisions
Symptom. Every decision flows through the PgM; teams cannot move forward until the PgM is available. Why it's bad. A bottlenecked PgM is a slow program. Worse, the PgM cannot scale to the level of detail teams need, so decisions slip in quality. The right model is decentralized decisions with a coordinating PgM. Bad example:
"All cross-team decisions: 'Wait for next PgM sync.' Teams idle 3-5 days waiting."
Good example:
"Decision rights documented per area (DACI matrix in the charter). Most decisions are made by the responsible team lead, with the PgM informed. PgM-driven decisions are reserved for cross-team trade-offs (scope swaps, resource reallocations). Async-by-default; sync only for genuine forks."
How to catch it. Team-lead-level decisions queued behind PgM = re-clarify DACI.
---
Red Flag 9: Cross-Team Communication Sent Once
Symptom. Major decision communicated in one Slack channel at one moment; the program assumes everyone read it. Why it's bad. Cross-team communications need multiple channels, multiple times, with confirmation. Single-broadcast decisions get missed by the team that was in a deep-work block. Two weeks later, a team is acting on the old assumption. Bad example:
"Decision: switch vendor. Sent once in #program-channel. Three teams missed it; one team built against the old vendor's API for 2 weeks."
Good example:
"Decision communication policy: (1) Slack #program-decisions thread with the decision; (2) email summary to team leads; (3) update the program charter / decision log; (4) confirmation read-receipts from each team lead. Decisions affecting > 2 teams get a brief sync to ensure shared interpretation."
How to catch it. Major decision communicated only once = communicate twice with confirmation.
---
Red Flag 10: Program Close Without a Retro
Symptom. Program ships; team disbands; no retro is scheduled. Why it's bad. A program is the org's biggest learning opportunity. Skipping the retro means the same dependency snags, the same RAID failures, the same scope creep recur in the next program. Bad example:
"Program shipped 2 weeks ago. Team disbanded. No retro held."
Good example:
"Program retro held within 3 weeks of close, with all team leads. Three artifacts: (1) what worked; (2) what failed (with root causes, not blame); (3) 3-5 concrete changes for the next program. Outputs feed the program-management playbook. PgM publishes within 1 week of the retro."
How to catch it. Program closed without a scheduled retro = schedule it now.
---
Red Flag Quick Reference
| # | Anti-pattern | One-line check |
|---|---|---|
| 1 | Dependency theater | Sync meeting produces decisions, not readouts |
| 2 | RAID log nobody reads | < 30 active entries, weekly review |
| 3 | Status without decisions | Steering deck has explicit Asks |
| 4 | Mid-program dependency surprises | Week-0 reconciliation workshop |
| 5 | Critical path lost | Recomputed weekly |
| 6 | Risks without tripwires | Every risk has a tripwire column |
| 7 | No program charter | Charter signed at kickoff |
| 8 | PgM as decision bottleneck | DACI clarifies decision rights |
| 9 | Single-broadcast comms | Multi-channel with read confirmation |
| 10 | No close retro | Retro within 3 weeks of close |
Related Reading
SKILL.md-- program management frameworkreferences/charter-template.md-- the charter documentreferences/raid-template.md-- the RAID log structure- Sibling skill:
execution/dependency-map/-- the dependency graph tool - Sibling skill:
execution/daci-framework/-- decision rights matrix - Sibling skill:
execution/status-update-generator/-- program-level status reporting - Sibling skill:
senior-pm/-- stakeholder mapping for the steering committee - Sibling skill:
delivery-manager/-- release coordination within the program
#!/usr/bin/env python3
"""Milestone Tracker - Track program milestones with status and trend analysis.
Reads milestone data and produces a timeline view with status indicators,
critical path highlighting, and schedule health assessment.
Usage:
python milestone_tracker.py --milestones milestones.json
python milestone_tracker.py --milestones milestones.json --json
python milestone_tracker.py --example
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
def load_data(path: str) -> dict:
"""Load milestone data from JSON file."""
with open(path, "r") as f:
return json.load(f)
def parse_date(s: str) -> datetime:
"""Parse date string."""
for fmt in ("%Y-%m-%d", "%Y-%m-%dT%H:%M:%S"):
try:
return datetime.strptime(s, fmt)
except ValueError:
continue
raise ValueError(f"Cannot parse date: {s}")
def analyze_milestones(data: dict) -> dict:
"""Analyze milestone status and trends."""
program = data.get("program", "Unknown")
milestones = data.get("milestones", [])
today = datetime.now()
results = []
total = len(milestones)
completed = 0
on_track = 0
at_risk = 0
overdue = 0
total_variance_days = 0
for ms in milestones:
name = ms.get("name", "Unknown")
project = ms.get("project", "Unknown")
planned_str = ms.get("planned_date", "")
actual_str = ms.get("actual_date", "")
status = ms.get("status", "Not Started")
is_critical = ms.get("critical_path", False)
dependencies = ms.get("dependencies", [])
planned = parse_date(planned_str) if planned_str else None
actual = parse_date(actual_str) if actual_str else None
# Calculate variance
variance_days = 0
if status == "Complete" and planned and actual:
variance_days = (actual - planned).days
completed += 1
elif status == "In Progress" and planned:
days_until = (planned - today).days
if days_until < 0:
variance_days = days_until
overdue += 1
elif days_until <= 7:
at_risk += 1
else:
on_track += 1
elif status == "Not Started" and planned:
days_until = (planned - today).days
if days_until < 0:
variance_days = days_until
overdue += 1
else:
on_track += 1
total_variance_days += abs(variance_days)
# RAG for this milestone
if status == "Complete":
if variance_days <= 0:
rag = "GREEN"
elif variance_days <= 5:
rag = "AMBER"
else:
rag = "RED"
elif status == "In Progress":
if planned and (planned - today).days < 0:
rag = "RED"
elif planned and (planned - today).days <= 7:
rag = "AMBER"
else:
rag = "GREEN"
else:
if planned and (planned - today).days < 0:
rag = "RED"
else:
rag = "GREEN"
results.append({
"name": name,
"project": project,
"planned_date": planned_str,
"actual_date": actual_str if actual_str else None,
"status": status,
"rag": rag,
"variance_days": variance_days,
"critical_path": is_critical,
"dependencies": dependencies,
})
# Sort by planned date
results.sort(key=lambda x: x["planned_date"] if x["planned_date"] else "9999-99-99")
# Schedule health
not_completed = total - completed
if overdue > 0:
schedule_health = "AT RISK"
elif at_risk > not_completed * 0.3:
schedule_health = "CAUTION"
else:
schedule_health = "ON TRACK"
# Critical path analysis
critical_milestones = [m for m in results if m["critical_path"]]
critical_overdue = [m for m in critical_milestones if m["rag"] == "RED"]
# Recommendations
recs = []
if critical_overdue:
recs.append(f"{len(critical_overdue)} critical-path milestone(s) are overdue. This directly impacts the program end date -- escalate immediately.")
if overdue > 0:
recs.append(f"{overdue} milestone(s) overdue. Review scope, add resources, or negotiate new dates with stakeholders.")
if at_risk > 0:
recs.append(f"{at_risk} milestone(s) due within 7 days. Confirm delivery readiness and clear any blockers.")
upcoming_7d = [m for m in results if m["status"] != "Complete" and m["planned_date"] and 0 <= (parse_date(m["planned_date"]) - today).days <= 7]
if upcoming_7d:
recs.append(f"Upcoming this week: {', '.join(m['name'] for m in upcoming_7d)}")
return {
"program": program,
"date": today.strftime("%Y-%m-%d"),
"schedule_health": schedule_health,
"total_milestones": total,
"completed": completed,
"on_track": on_track,
"at_risk": at_risk,
"overdue": overdue,
"completion_pct": round(completed / total * 100, 1) if total > 0 else 0,
"milestones": results,
"critical_path_count": len(critical_milestones),
"critical_overdue": len(critical_overdue),
"recommendations": recs,
}
def print_report(result: dict) -> None:
"""Print human-readable milestone report."""
print(f"\nMilestone Tracker: {result['program']}")
print(f"Date: {result['date']}")
print("=" * 75)
print(f"Schedule Health: {result['schedule_health']}")
print(f"Milestones: {result['completed']}/{result['total_milestones']} complete ({result['completion_pct']:.0f}%)")
print(f" On Track: {result['on_track']} | At Risk: {result['at_risk']} | Overdue: {result['overdue']}")
print(f" Critical Path: {result['critical_path_count']} milestones ({result['critical_overdue']} overdue)")
print(f"\nTimeline:")
print(f" {'Status':<6} {'Milestone':<30} {'Project':<15} {'Planned':<12} {'Actual':<12} {'Var':>5} {'CP'}")
print(f" {'-'*6} {'-'*30} {'-'*15} {'-'*12} {'-'*12} {'-'*5} {'-'*3}")
for m in result["milestones"]:
name = m["name"][:28] + ".." if len(m["name"]) > 30 else m["name"]
project = m["project"][:13] + ".." if len(m["project"]) > 15 else m["project"]
actual = m["actual_date"] if m["actual_date"] else "---"
var = f"{m['variance_days']:+d}d" if m["variance_days"] != 0 else "0d"
cp = "*" if m["critical_path"] else ""
rag_icon = {"GREEN": "[G]", "AMBER": "[A]", "RED": "[R]"}.get(m["rag"], "[?]")
print(f" {rag_icon:<6} {name:<30} {project:<15} {m['planned_date']:<12} {actual:<12} {var:>5} {cp}")
if result["recommendations"]:
print(f"\nRecommendations:")
for i, r in enumerate(result["recommendations"], 1):
print(f" {i}. {r}")
print()
def print_example() -> None:
"""Print example milestone data JSON."""
example = {
"program": "Digital Transformation",
"milestones": [
{"name": "Requirements Complete", "project": "API Migration", "planned_date": "2026-02-01", "actual_date": "2026-02-03", "status": "Complete", "critical_path": True, "dependencies": []},
{"name": "API v2 Released", "project": "API Migration", "planned_date": "2026-03-15", "actual_date": "2026-03-14", "status": "Complete", "critical_path": True, "dependencies": ["Requirements Complete"]},
{"name": "Beta Launch", "project": "Frontend Rebuild", "planned_date": "2026-04-01", "status": "In Progress", "critical_path": True, "dependencies": ["API v2 Released"]},
{"name": "Data Schema Finalized", "project": "Analytics Platform", "planned_date": "2026-03-20", "status": "In Progress", "critical_path": False, "dependencies": []},
{"name": "UAT Complete", "project": "Frontend Rebuild", "planned_date": "2026-04-15", "status": "Not Started", "critical_path": True, "dependencies": ["Beta Launch"]},
{"name": "Go Live", "project": "Frontend Rebuild", "planned_date": "2026-05-01", "status": "Not Started", "critical_path": True, "dependencies": ["UAT Complete"]},
],
}
print(json.dumps(example, indent=2))
def main():
parser = argparse.ArgumentParser(
description="Track program milestones with status and trend analysis."
)
parser.add_argument("--milestones", type=str, help="Path to milestones JSON file")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
parser.add_argument("--example", action="store_true", help="Print example milestones JSON and exit")
args = parser.parse_args()
if args.example:
print_example()
return
if not args.milestones:
parser.error("--milestones is required (use --example to see the expected format)")
data = load_data(args.milestones)
result = analyze_milestones(data)
if args.json:
print(json.dumps(result, indent=2))
else:
print_report(result)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Portfolio Dashboard - Generate program portfolio status dashboard.
Reads program/project data and produces a RAG-status dashboard with
milestone tracking, budget analysis, and risk summary.
Usage:
python portfolio_dashboard.py --portfolio portfolio.json
python portfolio_dashboard.py --portfolio portfolio.json --json
python portfolio_dashboard.py --example
"""
import argparse
import json
import sys
from datetime import datetime
def load_data(path: str) -> dict:
"""Load portfolio data from JSON file."""
with open(path, "r") as f:
return json.load(f)
def classify_rag(schedule_variance_days: int, budget_pct: float, risks_high: int) -> str:
"""Classify project RAG status."""
if schedule_variance_days > 15 or budget_pct > 110 or risks_high >= 3:
return "RED"
elif schedule_variance_days > 5 or budget_pct > 100 or risks_high >= 1:
return "AMBER"
return "GREEN"
def analyze_portfolio(data: dict) -> dict:
"""Analyze portfolio and produce dashboard data."""
portfolio_name = data.get("name", "Unknown Portfolio")
programs = data.get("programs", [])
program_results = []
total_budget = 0
total_spent = 0
all_milestones = 0
completed_milestones = 0
rag_counts = {"GREEN": 0, "AMBER": 0, "RED": 0}
for prog in programs:
name = prog.get("name", "Unknown")
projects = prog.get("projects", [])
prog_budget = prog.get("budget", 0)
prog_spent = prog.get("spent", 0)
budget_pct = round(prog_spent / prog_budget * 100, 1) if prog_budget > 0 else 0
schedule_var = prog.get("schedule_variance_days", 0)
high_risks = prog.get("high_risks", 0)
rag = classify_rag(schedule_var, budget_pct, high_risks)
rag_counts[rag] = rag_counts.get(rag, 0) + 1
total_budget += prog_budget
total_spent += prog_spent
# Project details
project_details = []
for proj in projects:
p_milestones = proj.get("milestones_total", 0)
p_completed = proj.get("milestones_completed", 0)
all_milestones += p_milestones
completed_milestones += p_completed
p_pct = round(p_completed / p_milestones * 100) if p_milestones > 0 else 0
project_details.append({
"name": proj.get("name", "Unknown"),
"status": proj.get("status", "In Progress"),
"completion_pct": p_pct,
"next_milestone": proj.get("next_milestone", "N/A"),
"next_milestone_date": proj.get("next_milestone_date", "TBD"),
})
# Benefits
benefits_target = prog.get("benefits_target", 0)
benefits_realized = prog.get("benefits_realized", 0)
benefits_pct = round(benefits_realized / benefits_target * 100, 1) if benefits_target > 0 else 0
program_results.append({
"name": name,
"rag_status": rag,
"schedule_variance_days": schedule_var,
"budget": prog_budget,
"spent": prog_spent,
"budget_pct": budget_pct,
"high_risks": high_risks,
"benefits_target": benefits_target,
"benefits_realized": benefits_realized,
"benefits_pct": benefits_pct,
"projects": project_details,
"phase": prog.get("phase", "Execution"),
})
# Portfolio-level metrics
portfolio_budget_pct = round(total_spent / total_budget * 100, 1) if total_budget > 0 else 0
milestone_pct = round(completed_milestones / all_milestones * 100, 1) if all_milestones > 0 else 0
# Overall portfolio health
if rag_counts.get("RED", 0) > 0:
portfolio_health = "AT RISK"
elif rag_counts.get("AMBER", 0) > len(programs) / 2:
portfolio_health = "CAUTION"
else:
portfolio_health = "ON TRACK"
# Recommendations
recs = []
red_programs = [p for p in program_results if p["rag_status"] == "RED"]
if red_programs:
for rp in red_programs:
recs.append(f"'{rp['name']}' is RED -- escalate to steering committee with recovery plan.")
amber_long = [p for p in program_results if p["rag_status"] == "AMBER" and p["schedule_variance_days"] > 10]
if amber_long:
recs.append(f"{len(amber_long)} program(s) trending toward RED. Review scope and resource options.")
if portfolio_budget_pct > 90:
recs.append(f"Portfolio budget is {portfolio_budget_pct:.0f}% consumed. Review remaining scope against available budget.")
return {
"portfolio": portfolio_name,
"date": datetime.now().strftime("%Y-%m-%d"),
"health": portfolio_health,
"program_count": len(programs),
"rag_distribution": rag_counts,
"budget": {"total": total_budget, "spent": total_spent, "pct": portfolio_budget_pct},
"milestones": {"total": all_milestones, "completed": completed_milestones, "pct": milestone_pct},
"programs": program_results,
"recommendations": recs,
}
def print_report(result: dict) -> None:
"""Print human-readable portfolio dashboard."""
print(f"\nPortfolio Dashboard: {result['portfolio']}")
print(f"Date: {result['date']} | Programs: {result['program_count']}")
print("=" * 70)
print(f"Portfolio Health: {result['health']}")
rag = result["rag_distribution"]
print(f"RAG: GREEN={rag.get('GREEN',0)} | AMBER={rag.get('AMBER',0)} | RED={rag.get('RED',0)}")
b = result["budget"]
print(f"Budget: ${b['spent']:,.0f} / ${b['total']:,.0f} ({b['pct']:.1f}%)")
m = result["milestones"]
print(f"Milestones: {m['completed']}/{m['total']} ({m['pct']:.1f}%)")
for prog in result["programs"]:
print(f"\n [{prog['rag_status']}] {prog['name']} (Phase: {prog['phase']})")
print(f" Schedule: {prog['schedule_variance_days']:+d} days | Budget: {prog['budget_pct']:.0f}% | Risks: {prog['high_risks']} high")
print(f" Benefits: ${prog['benefits_realized']:,.0f} / ${prog['benefits_target']:,.0f} ({prog['benefits_pct']:.0f}%)")
for proj in prog["projects"]:
print(f" - {proj['name']}: {proj['status']} ({proj['completion_pct']}%) Next: {proj['next_milestone']} ({proj['next_milestone_date']})")
if result["recommendations"]:
print(f"\nRecommendations:")
for i, r in enumerate(result["recommendations"], 1):
print(f" {i}. {r}")
print()
def print_example() -> None:
"""Print example portfolio JSON."""
example = {
"name": "Digital Transformation Portfolio",
"programs": [
{
"name": "Customer Platform Modernization",
"phase": "Execution",
"budget": 2000000,
"spent": 1400000,
"schedule_variance_days": -3,
"high_risks": 1,
"benefits_target": 5000000,
"benefits_realized": 1200000,
"projects": [
{"name": "API Migration", "status": "Complete", "milestones_total": 4, "milestones_completed": 4, "next_milestone": "N/A", "next_milestone_date": "Done"},
{"name": "Frontend Rebuild", "status": "In Progress", "milestones_total": 5, "milestones_completed": 3, "next_milestone": "Beta Release", "next_milestone_date": "2026-04-15"},
],
},
{
"name": "Data Analytics Platform",
"phase": "Planning",
"budget": 800000,
"spent": 120000,
"schedule_variance_days": 0,
"high_risks": 0,
"benefits_target": 2000000,
"benefits_realized": 0,
"projects": [
{"name": "Data Lake Setup", "status": "In Progress", "milestones_total": 3, "milestones_completed": 1, "next_milestone": "Schema Design Complete", "next_milestone_date": "2026-04-01"},
],
},
],
}
print(json.dumps(example, indent=2))
def main():
parser = argparse.ArgumentParser(
description="Generate program portfolio status dashboard."
)
parser.add_argument("--portfolio", type=str, help="Path to portfolio data JSON file")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
parser.add_argument("--example", action="store_true", help="Print example portfolio JSON and exit")
args = parser.parse_args()
if args.example:
print_example()
return
if not args.portfolio:
parser.error("--portfolio is required (use --example to see the expected format)")
data = load_data(args.portfolio)
result = analyze_portfolio(data)
if args.json:
print(json.dumps(result, indent=2))
else:
print_report(result)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Resource Allocator - Analyze and optimize resource allocation across projects.
Reads resource allocation data and identifies over-allocation, conflicts,
utilization gaps, and produces rebalancing recommendations.
Usage:
python resource_allocator.py --resources resources.json
python resource_allocator.py --resources resources.json --json
python resource_allocator.py --example
"""
import argparse
import json
import sys
from collections import defaultdict
def load_data(path: str) -> dict:
"""Load resource data from JSON file."""
with open(path, "r") as f:
return json.load(f)
def analyze_resources(data: dict) -> dict:
"""Analyze resource allocation and identify conflicts."""
program = data.get("program", "Unknown")
resources = data.get("resources", [])
projects = data.get("projects", [])
# Build allocation map: person -> list of allocations
person_allocations = defaultdict(list)
project_allocations = defaultdict(list)
for alloc in data.get("allocations", []):
person = alloc.get("person", "Unknown")
project = alloc.get("project", "Unknown")
pct = alloc.get("allocation_pct", 0)
role = alloc.get("role", "Team Member")
period = alloc.get("period", "Current")
person_allocations[person].append({
"project": project,
"allocation_pct": pct,
"role": role,
"period": period,
})
project_allocations[project].append({
"person": person,
"allocation_pct": pct,
"role": role,
})
# Person-level analysis
person_results = []
over_allocated = []
under_allocated = []
for person, allocs in person_allocations.items():
total_pct = sum(a["allocation_pct"] for a in allocs)
project_count = len(allocs)
status = "Balanced"
if total_pct > 100:
status = "Over-Allocated"
over_allocated.append({"person": person, "total_pct": total_pct, "projects": project_count})
elif total_pct < 50:
status = "Under-Utilized"
under_allocated.append({"person": person, "total_pct": total_pct, "projects": project_count})
elif total_pct < 70:
status = "Available Capacity"
# Context switching penalty
switching_penalty = 0
if project_count >= 4:
switching_penalty = 40
elif project_count == 3:
switching_penalty = 20
elif project_count == 2:
switching_penalty = 10
effective_capacity = max(0, total_pct - switching_penalty)
person_results.append({
"person": person,
"total_allocation_pct": total_pct,
"effective_capacity_pct": effective_capacity,
"project_count": project_count,
"context_switching_penalty_pct": switching_penalty,
"status": status,
"allocations": allocs,
})
person_results.sort(key=lambda x: x["total_allocation_pct"], reverse=True)
# Project-level analysis
project_results = []
for proj_name in set(a["project"] for allocs in person_allocations.values() for a in allocs):
allocs = project_allocations.get(proj_name, [])
total_fte = sum(a["allocation_pct"] for a in allocs) / 100
headcount = len(allocs)
roles = list(set(a["role"] for a in allocs))
project_results.append({
"project": proj_name,
"total_fte": round(total_fte, 1),
"headcount": headcount,
"roles": roles,
"team": [{"person": a["person"], "pct": a["allocation_pct"], "role": a["role"]} for a in allocs],
})
project_results.sort(key=lambda x: x["total_fte"], reverse=True)
# Summary metrics
total_people = len(person_allocations)
total_fte = sum(p["total_allocation_pct"] for p in person_results) / 100
avg_utilization = sum(p["total_allocation_pct"] for p in person_results) / total_people if total_people > 0 else 0
avg_projects_per_person = sum(p["project_count"] for p in person_results) / total_people if total_people > 0 else 0
# Recommendations
recs = []
if over_allocated:
for oa in over_allocated:
recs.append(f"{oa['person']} is at {oa['total_pct']}% across {oa['projects']} projects. Reduce allocation to <=100% to prevent burnout and quality issues.")
if under_allocated:
names = [ua["person"] for ua in under_allocated]
recs.append(f"Under-utilized: {', '.join(names)}. Consider reallocating to projects with resource gaps.")
high_switching = [p for p in person_results if p["context_switching_penalty_pct"] >= 20]
if high_switching:
for hs in high_switching:
recs.append(f"{hs['person']} works on {hs['project_count']} projects (effective capacity: {hs['effective_capacity_pct']}%). Consolidate to max 2 projects for productivity.")
if avg_utilization > 95:
recs.append("Team is near full utilization with no slack. Add buffer capacity (aim for 80-85% utilization) to absorb unplanned work.")
if not recs:
recs.append("Resource allocation looks balanced. Review monthly to catch emerging conflicts early.")
return {
"program": program,
"summary": {
"total_people": total_people,
"total_fte": round(total_fte, 1),
"avg_utilization_pct": round(avg_utilization, 1),
"avg_projects_per_person": round(avg_projects_per_person, 1),
"over_allocated_count": len(over_allocated),
"under_utilized_count": len(under_allocated),
},
"people": person_results,
"projects": project_results,
"recommendations": recs,
}
def print_report(result: dict) -> None:
"""Print human-readable resource allocation report."""
s = result["summary"]
print(f"\nResource Allocation: {result['program']}")
print("=" * 70)
print(f"People: {s['total_people']} | Total FTE: {s['total_fte']} | Avg Utilization: {s['avg_utilization_pct']:.0f}%")
print(f"Avg Projects/Person: {s['avg_projects_per_person']:.1f} | Over-allocated: {s['over_allocated_count']} | Under-utilized: {s['under_utilized_count']}")
print(f"\nPerson Allocation:")
print(f" {'Person':<20} {'Total':>6} {'Effective':>10} {'Projects':>9} {'Status'}")
print(f" {'-'*20} {'-'*6} {'-'*10} {'-'*9} {'-'*15}")
for p in result["people"]:
print(f" {p['person']:<20} {p['total_allocation_pct']:>5}% {p['effective_capacity_pct']:>9}% {p['project_count']:>9} {p['status']}")
print(f"\nProject Staffing:")
for proj in result["projects"]:
print(f" {proj['project']}: {proj['total_fte']:.1f} FTE ({proj['headcount']} people)")
for member in proj["team"]:
print(f" - {member['person']}: {member['pct']}% ({member['role']})")
if result["recommendations"]:
print(f"\nRecommendations:")
for i, r in enumerate(result["recommendations"], 1):
print(f" {i}. {r}")
print()
def print_example() -> None:
"""Print example resource data JSON."""
example = {
"program": "Digital Transformation",
"allocations": [
{"person": "Alice Chen", "project": "API Migration", "allocation_pct": 80, "role": "Tech Lead"},
{"person": "Alice Chen", "project": "Frontend Rebuild", "allocation_pct": 20, "role": "Advisor"},
{"person": "Bob Martinez", "project": "Frontend Rebuild", "allocation_pct": 100, "role": "Engineer"},
{"person": "Carol Davis", "project": "API Migration", "allocation_pct": 50, "role": "Engineer"},
{"person": "Carol Davis", "project": "Analytics Platform", "allocation_pct": 50, "role": "Engineer"},
{"person": "Carol Davis", "project": "DevOps Modernization", "allocation_pct": 30, "role": "Consultant"},
{"person": "Dave Wilson", "project": "Analytics Platform", "allocation_pct": 40, "role": "Data Engineer"},
],
}
print(json.dumps(example, indent=2))
def main():
parser = argparse.ArgumentParser(
description="Analyze and optimize resource allocation across projects."
)
parser.add_argument("--resources", type=str, help="Path to resource allocation JSON file")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
parser.add_argument("--example", action="store_true", help="Print example resource data and exit")
args = parser.parse_args()
if args.example:
print_example()
return
if not args.resources:
parser.error("--resources is required (use --example to see the expected format)")
data = load_data(args.resources)
result = analyze_resources(data)
if args.json:
print(json.dumps(result, indent=2))
else:
print_report(result)
if __name__ == "__main__":
main()