---
name: cse-bandwidth
description: Produce a report-only CSE bandwidth/capacity-risk assessment. Use when the user asks for CSE bandwidth, strapped score, capacity risk, workload risk, who is overloaded, or how loaded each CSE is. The skill reads Jira, Slack, Kepler/Gong/email, GitHub, and optional Granola evidence, then outputs a 1-10 internal capacity/load-risk report with confidence and source-grouped drivers. It never reassigns work, updates Jira, sends Slack messages, contacts customers, judges performance, or predicts customer impact.
argument-hint: "[--days 14] [--max-cses N] [--json]"
---

# CSE Bandwidth Report

## Compact MCP routing

Follow the shared [compact MCP routing contract](../../shared/compact-mcp-routing.md). Interactive facade tools are `cse_capabilities`, `cse_read`, `cse_apply`, `context_assemble`, and `cse_session_info`; named operations are capability ids. Call reads through `cse_read` with the capability id. This skill has no write path, but every mutation would go through `cse_apply` twice: dry-run first, then the identical capability and arguments with `execute:true`, justification, and the returned `preview_digest`. Call `context_assemble` and `cse_session_info` directly when needed.


Produce an internal, report-only CSE capacity/load-risk assessment. Score every
current member of the live CSE roster on a 1-10 "strapped" scale, including people
with zero assigned Jira work, explain the score with source-grouped evidence, and
recommend human follow-up for high or same-run approaching-capacity cases.

The score is a **capacity/load-risk heuristic**. Jira work is converted into effective work units, so lined-up intake, duplicate same-customer workstreams, and future-gated/waiting tickets each weigh less than a separate active delivery engagement.

The best report is **context-first**. Programmatic counts establish the baseline; read-only investigation then connects Jira, Slack, Gong/email, GitHub, and Granola evidence into a structured context assessment that caps, floors, or explains the load read.

This is **not** an employee performance report. It is **not** a customer-impact, churn, or escalation-likelihood predictor. It does **not** take action.

## Hard boundaries

Do not perform any external writes:

- No Jira reassignment, transition, field edit, or comment.
- No Slack message, reaction, DM, channel post, or stakeholder ping.
- No customer-facing message.
- No automatic owner/load rebalancing.
- No performance/effectiveness judgment language.
- No customer-impact/churn/escalation-probability prediction.

If a follow-up is warranted, write it as a recommendation for a human, for example: "Discuss whether to stop pulling this CSE into new novel work this week."

This skill is read-only by construction. Every tool it calls is a read or search. The Jira, Slack, and Granola write tools and `gh` write/PR-create subcommands are out of scope.

## Setup

From the repo root, load the shared skill bootstrap so `CSE_TOOLD_BIN` and `CSE_OPERATOR` resolve:

```sh
source "${PLUGIN_ROOT:-${DROID_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-$PWD/plugins/cse-tools}}}/.agents/shared/skill-bootstrap.sh"
```

The daemon keeps the keychain entries that back Jira, Slack, Kepler, and Granola fresh on its own through the local `cse-tools` MCP; no mint step is needed. On a 401, call `cse_session_info({"force_refresh": true})` once and retry. A 403 means access is blocked or revoked: surface the named operator action and stop without retrying. GitHub uses the `gh` CLI authed against the `postman-cs` org (admin:org); it does not depend on the daemon. Granola is optional and frequently not enrolled in a given session; treat it as a supplemental source and continue without it.

The daemon owns domain and credential resolution; use semantic Jira fields and `cse_domain_info`.

- **Read strategy:** Follow [`.agents/shared/read-strategy.md`](../../shared/read-strategy.md). Establish the Jira population baseline with `jira_search`. Per-CSE evidence may use `context_assemble({"entity":{"kind":"operator","ref":"<name-or-email>"}})` to collapse the Slack/Kepler/Granola fan-out, but assembled cards and model investigators are advisory only. The deterministic rubric below owns every score. GitHub attribution follows [`.agents/shared/builder-evidence.md`](../../shared/builder-evidence.md) via direct `gh`.
- **Tool frugality:** Follow [`.agents/shared/tool-frugality.md`](../../shared/tool-frugality.md). Request only the Jira fields and bounded pages needed for the baseline. Hydrate only communications or Granola notes that will support a reported finding; for Kepler content, read either `body` or `transcript`, never both.

## Data sources

All platform reads use direct tool calls, `context_assemble`, or the `gh` CLI. There are no Node helper scripts; the model collects evidence and applies the scoring rubric directly.

- **Jira** (required): `jira_search` and `jira_get_issue` direct reads. Use `named_query` and semantic `fields` names; the daemon maps field ids and expands JQL. Use `cse_domain_info` for stage/lifecycle discovery. Pull open assigned work, recently completed work, and per-ticket stage/customer/Team ID detail.
- **Slack reads** (optional, recommended): local `cse-tools` MCP read tools only (`slack_search`, `slack_channels`, `slack_read_channel`, `slack_read_thread`, `slack_users`). No Slack write tools.
- **Kepler/Gong/email** (optional, recommended): `semantic_context_search` for source-linked CSE context packs, then `list_communications` and `get_communications` for specific transcript/body hydration. If Kepler returns 5xx or times out, mark transcript/body details unavailable and lower confidence.
- **GitHub** (optional, recommended): the `gh` CLI against `postman-cs` per [`.agents/shared/builder-evidence.md`](../../shared/builder-evidence.md). Build output is a capacity signal that Jira and call logs do not show. Honor the shared per-repo attribution rule or the counts will be wrong.
- **Granola** (optional): use `granola_list_meetings` for active-workspace attendee discovery, then batch selected IDs through `granola_get_meetings`. Use `granola_list_notes` only for an explicit cross-workspace gap. If unavailable, mark the source unavailable and continue.

## Intelligent context layer

Do not treat the score as a mechanical count of tickets/messages. After collecting the Jira population, launch bounded read-only investigators when the runtime supports subagents (prefer smaller/faster models for these narrow searches), or perform the same investigation manually when subagents are unavailable.

Investigators produce structured findings. The model converts those findings into score floors/caps and report evidence by applying the scoring rubric below; investigators do not set the final score directly.

Good investigator slices:

1. **Per-CSE investigator** classifies the CSE's assigned work as active delivery, bounded upcoming workshop/event prep, internal ops, lined-up/not-active, future-gated, customer-blocked, same-customer collapsed, advisory-only, or untracked active customer work.
2. **Same-customer cluster investigator** decides whether multiple Jira tickets for one customer/team are genuinely parallel workstreams or one project split across tickets.
3. **Untracked-work investigator** finds meaningful active customer work in Slack/Gong/GitHub/Granola where the CSE is involved but assigned Jira does not reflect the load.
4. **Counter-evidence investigator** finds reasons the score should be lower: future meetings, pending scope agreement, waiting on customer, no recent movement, duplicate same-customer work, customer-blocked status, or workshop/event prep that is bounded upcoming work.
5. **Novel/internal work investigator** identifies beta programs, customer-specific workaround design, peer-assist work, service-token/IAM pulls, internal enablement, and tooling/build work (often visible as GitHub commits to internal repos) that consumes capacity.

Strict read-only subagent prompt template:

```text
You are a read-only CSE bandwidth context investigator. Do not write Jira, Slack, email, GitHub, or customer-facing messages.

Scope:
- CSE: <name/email>
- Jira assigned issues/customers: <compact list>
- Lookback: <N days>
- Question: <one bounded question, e.g. "which of these are active delivery vs lined up/future-gated?">

Use only read-only evidence from Jira, Slack, Kepler/Gong/email, GitHub, and Granola. Prefer participant-verified evidence. If evidence conflicts, record counter-evidence instead of guessing.

Return only JSON:
{
  "summary": "...",
  "confidence": "low|medium|high",
  "activeDeliveryClusters": [{"customer":"...","issueKey":"...","summary":"..."}],
  "untrackedActiveCustomerWork": [{"customer":"...","summary":"...","evidence":"slack|gong|github|granola"}],
  "linedUpButNotActive": [{"customer":"...","summary":"..."}],
  "futureGated": [{"customer":"...","summary":"..."}],
  "customerBlocked": [{"customer":"...","summary":"..."}],
  "sameCustomerCollapsedWork": [{"customer":"...","summary":"..."}],
  "boundedUpcomingWork": [{"customer":"...","summary":"..."}],
  "internalOpsWork": [{"summary":"..."}],
  "urgentEscalationSurges": [{"customer":"...","summary":"..."}],
  "advisoryOnlyPulls": [{"customer":"...","summary":"..."}],
  "novelInternalPulls": [{"summary":"...","evidence":"slack|github|granola"}],
  "counterEvidence": [{"summary":"..."}],
  "recommendedScoreBand": {"band":"Low|Moderate|Watch|At-risk","min":null,"max":null,"confidence":"low|medium|high","why":"advisory only; the rubric drives the final score"}
}
```

Merge investigator JSON into `cseEvidence[<cse-key>].contextAssessment`. Treat `recommendedScoreBand` as advisory only; the structured findings drive scoring through the rubric.

## Scoring model

Build the population with `cse_orgchart_identity_snapshot`
and arguments `{"refresh":true}` from
the configured CSE roots, intersected with resolved active Slack identities. Jira is
joined onto that roster by work email; it does not define the population. Join
untracked Slack/communications/GitHub/Granola evidence for every roster member, so a
zero-Jira CSE remains present and can receive an untracked-work floor.

Convert Jira work into effective load by weighting each open ticket by stage and cluster:

- **Stage weights**: Submitted = 0.2, Qualification Review = 0.35, Technical Discovery = 0.7, Pilot Scoping = 0.6, Internal Proof = 0.9, Customer Implementation = 1.0, Pilot Validation = 0.75, Pattern/Asset work = 0.5.
- **Same customer/team discount**: tickets with the same Team ID, or same customer when Team ID is missing, are clustered. The highest-weight ticket counts fully; each additional ticket in that cluster counts at 35%.
- **Same-customer active update gate**: duplicate workstreams for one customer count as active pressure only when every duplicate ticket has recent movement. If only part of the duplicate cluster is fresh, the extra tickets are treated as lined-up work and drop out of concurrent context-switching load.
- **Lineup load**: Submitted and Qualification Review are reported separately as `lineup/intake` and should not be described as active delivery work.
- **Waiting/future-gated work**: customer-waiting, no-response, handoff, and future-meeting language lowers `actionable-now load` while still preserving the work in portfolio load.
- **Mid-project priority**: active mid-project delivery clusters should outrank a broader portfolio that is mostly stalled, pending scope agreement, or waiting on a future meeting. Stale waiting/future-gated issues are noted as board hygiene and stay out of same-day load pressure.
- **Urgent escalation surge**: one customer can temporarily dominate capacity when there is an executive-visible urgent blocker and the CSE is the clear owner. That low-breadth case can rise to Watch for 48 hours from the latest Jira update, then falls back to Moderate unless other active load is present.
- **Meeting load context**: Granola-heavy meeting weeks do not directly raise the score. When meeting/note volume is high, surface a summary note suggesting review of recurring/unnecessary meetings if they are not tied to active assigned engagements.
- **Sustained stagnation**: repeated weekly high scores do not inflate the current score. If the same active engagement set appears across three consecutive weekly reports with no Jira movement, add a prompt asking why work is not moving and whether Jira should reflect `Customer Blocked`.
- **Completion relief**: high completion velocity can reduce pressure when active delivery clusters materially drop. For example, going from 9 active delivery clusters to 5 after 4 completions should read as about a 10 to 7 relief move.
- **Customer Blocked**: Jira work in a customer-blocked status is ignored for capacity scoring, because it should represent no current CSE action available.
- **GitHub build signal**: sustained recent commits to a customer or demo repo with no matching active Jira are untracked active work and can floor the score (treat like `untrackedActiveCustomerWork`). Commits to internal tooling repos are novel/internal capacity context, capped below owned customer delivery. A young or recently unblocked GitHub history is not evidence of low effort (see the GitHub capacity caveat under Collect GitHub build evidence), and absence of commits never raises a score.
- **Context assessment**: read-only investigator findings have the largest calibration effect. Verified untracked active customer work and context-verified urgent surges can floor a score into Watch even when assigned Jira is light. High-confidence counter-evidence (future-gated, lined-up/not-active, customer-blocked, same-customer collapsed, or bounded upcoming workshop/event prep) can cap a programmatically high score back toward Low/Moderate. Internal ops work is real capacity context, but it is capped below active owned customer delivery. Advisory-only pulls count lightly only when paired with active owned delivery; otherwise they remain context.
- **At-risk guardrail**: a mostly waiting/future-gated portfolio with less than 3 actionable-now effective units stays capped below At-risk unless the context assessment verifies substantial active delivery outside the Jira actionability signals.
- **External communications**: account-level Gong/email evidence should be participant-filtered where possible. Set `participantVerified: false` when the CSE was not verified as a call/email participant; discount those items.

Deterministic base score: let `E` be actionable-now effective units after stage
weights, clustering, and blocked/waiting exclusions. Compute
`base = clamp(1, 10, roundHalfUp(1 + 1.5 * E))`, where half-up means
`floor(x + 0.5)`. Apply named rules in this order: completion relief, urgent-surge
floor, verified untracked-customer-work floor, internal/advisory caps, counter-
evidence caps, then the At-risk guardrail. A verified untracked active customer
cluster contributes its stage-equivalent effective units; if its stage is unknown it
contributes 0.7, and at least one such cluster floors the final score at 6 (Watch).
Completion relief subtracts `min(3, floor(completions / 2))` only when current active
clusters are at least two below the prior report, never below 1. Final tiers are:
1-3 Low, 4-5 Moderate, 6-7 Watch / approaching capacity, 8-10 At-risk.

Always show raw open count, customer/team cluster count, effective open load, actionable-now load, active delivery clusters, lineup count, waiting/future-gated count, and same-customer duplicate clusters when present.

## Workflow

### 1. Collect Jira population and pressure signals

Use `jira_search`, `named_query`, and semantic `fields` names; the daemon maps field ids and expands JQL. Use `cse_domain_info` for stage/lifecycle discovery. Run three separate read-only passes so lineup, active, and recently completed work stay distinguishable. Calls shown below use direct calls:

- Open assigned work (the CSE population and active load):

  ```text
  jira_search({
    "named_query":"all_open",
    "fields":["key","summary","status","assignee","reporter","updated","created",
              "customer_name","team_id"],
    "maxResults":100
  })
  ```

  `customer_name` and `team_id` are semantic field names (the daemon maps them to customfield ids); cluster on `team_id`, falling back to `customer_name` when Team ID is missing.
- Recent completions (for completion relief), last N days: `status CHANGED TO "Closed - Complete" AFTER -<N>d`.
- Per-ticket detail when stage, Team ID, goal, or success metrics matter: `jira_get_issue(key, fields=[...])`.

Group by assignee. For each CSE, compute the load profile from the stage weights and clustering rules above. Treat `Submitted` and `Qualification Review` as lineup/intake; do not count them toward active delivery.

### 2. Collect optional Slack evidence

For each CSE/customer from the Jira population, use read-only Slack MCP searches. Good query shapes:

- CSE mentions: `from:<@USERID> after:<YYYY-MM-DD>` to pull a CSE's own recent footprint, and `"<CSE name>" (beta OR pilot OR blocked OR escalation OR help OR service-token)`.
- Customer/project asks: `"<customer name>" "<CSE name>"`.
- Novel/internal project pulls: `"service-token" "<CSE name>"`, `"beta" "<CSE name>"`, `"IAM" "<CSE name>"`.

Resolve names to ids with `slack_users(action="search", query=<name-or-email>)` first. Normalize useful hits into `cseEvidence[<cse-email-or-key>].slack` and/or `.novel`. A low Slack footprint is itself a signal (heads-down execution); record it, do not treat it as low load.

### 3. Collect optional Kepler/Gong/email evidence

Use Kepler as primary. Honor the participant gotcha or the pull comes back empty.

1. `list_communications` with `participant_email=<email>`, `type=call`, `start_date=<today minus N days>`, `limit=20`. Do not use `assignee_email` for a CSE; that path resolves Salesforce GTM roles only and returns empty.
2. `get_communications(ids=[...], include_content="full", include_participants=true, content_max_chars=6000)` on only the richest ids that will support a finding. Run `type=email` too when calls are thin. A bounded `semantic_context_search(account_ids=[...], query="active work / blockers / workload", max_cards=8, snippet_chars=500)` fills topic gaps.
3. Field-name gotcha on `list_communications`: the `fields` enum expects `occurred_at`; passing `timestamp` errors the call.
4. Do **not** use `list_team_communications`; the team index is unreliable.

Split participants by email domain (postman.com internal, customer domain external) to recover accounts touched and the account team. If you only found account-level communication without verifying the CSE participated, include it with `"participantVerified": false` so it contributes as context at reduced weight. If the Kepler upstream returns 5xx or times out, keep list-level counts/recency, mark transcript details unavailable, and lower confidence. Normalize into `cseEvidence[<cse-email-or-key>].communications`.

### 4. Collect GitHub build evidence

Follow [`.agents/shared/builder-evidence.md`](../../shared/builder-evidence.md)
for live-roster work-email attribution, authenticated `gh`, complete repo-list
pagination, no-clone, no org-wide commit search, customer vs internal scope, and
degradation. Build output shows where effort is going when Jira and call logs do
not. Bandwidth-specific rules on top of that contract:

- Default lookback matches the report window (typically 14 days; accept 7-30).
  Preflight `gh`, paginate the full `postman-cs` repo list once, then attribute
  commits **per repo** after an exact work-email match retains the verified
  login. Abort if any repo-list page fails rather than scoring a truncated list.
  Run selected-repo reads concurrently (throttled to ~12) for speed.
- Example per-repo count after the shared complete list is on disk:

  ```sh
  while IFS= read -r r; do
    n=$(gh api --paginate "repos/postman-cs/$r/commits?author=<login>&since=<ISO>" \
          --jq '.[].sha' 2>/dev/null | wc -l | tr -d ' ')
    [ "${n:-0}" -gt 0 ] && echo "$n $r"
  done < /tmp/repos.txt | sort -rn
  ```

- CSEs sometimes also commit under a personal email; record those logins as
  history notes only. Capacity scoring attributes load only after the shared
  exact work-email match.
- **Capacity caveat:** a recently unblocked GitHub account (for example, one
  freed by an IT unblock ticket) has a short history. Low commits there do not
  mean low effort, and much CSE build output lives outside `postman-cs`
  (personal repos, Postman workspaces, slide decks, generated artifacts). Read
  commits as a positive signal of where effort is going; absence of commits
  never raises a score.
- Read the top one-line subjects of the heaviest repo to characterize the work
  (customer pilot vs internal tooling). Normalize into
  `cseEvidence[<cse-email-or-key>].github`, and route customer-repo work with no
  matching active Jira to `untrackedActiveCustomerWork`, internal-repo work to
  `novel`.

### 5. Collect optional Granola evidence

If Granola is enrolled, use `granola_list_meetings` with a bounded range and filter its attendee metadata for the CSE, then batch selected IDs through `granola_get_meetings`. Use the REST `granola_list_notes` path only when explicit cross-workspace coverage is needed. Normalize useful meetings into `.granola`, or `.novel` when they show informal/internal project pulls. If unavailable, set Granola source status to unavailable and continue.

### 6. Score and assemble the report

There is no scoring binary. Apply the deterministic formula directly to the assembled evidence per CSE:

- Compute the Jira load profile (raw open, clusters, effective open load, actionable-now load, active delivery clusters, lineup, waiting/future-gated, duplicate clusters).
- Apply the floors, caps, gates, completion relief, stagnation, and GitHub signal from the scoring model.
- Fold in the context-assessment findings (largest calibration effect).
- Land a 1-10 score and a risk tier, and record the source-grouped drivers behind it.

Track a `sourceStatuses` map so missing surfaces are visible:

```json
{
  "sourceStatuses": {
    "jira": { "status": "available" },
    "slack": { "status": "available" },
    "kepler": { "status": "available" },
    "github": { "status": "available" },
    "granola": { "status": "unavailable", "reason": "not enrolled this session" }
  }
}
```

For weekly comparisons, load exactly the latest three earlier
`cse-bandwidth-reports/YYYY-MM-DD.evidence.json` files by filename date (strictly
before this run), newest first. Each file must have `schemaVersion: 1`,
`generatedAt`, and `cses`, keyed by normalized work email. Each CSE value must contain
`score` (1-10), `activeClusters` (array of stable Team ID, else normalized customer,
plus sorted issue keys), `jiraUpdatedAtByIssue` (issue key to ISO timestamp), and
`completedIssueKeys` (array). Invalid/missing prior files are data gaps, not evidence.
Stagnation applies only when the current and all three prior files have an identical
`activeClusters` set and every issue's `updated` timestamp is unchanged across the
four snapshots. Completion relief compares current cluster count with the immediately
prior valid file and counts issue keys newly present in `completedIssueKeys`.

For user-facing runs, save artifacts under `cse-bandwidth-reports/`: `YYYY-MM-DD.md` for the report and, when structured context was gathered, `YYYY-MM-DD.evidence.json` for provenance. (That directory is gitignored so generated reports stay local.) Use `--json` framing when another tool should consume the output.

## Voice and report register

The markdown report is a status update for a human reader. Follow the shared register in `plugins/cse-tools/.agents/shared/prose-voice.md`:

- Sentence-case headings.
- Contractions on by default (`isn't`, `doesn't`, `I'd`, `we're`, `don't`, `can't`, `won't`).
- Double-hyphen asides (`--`), never Unicode em dashes.
- Start with a short `## My read` section that sounds like Jared thinking out loud: who to watch, why, and what to do next.
- Keep the scorecard auditable, and phrase labels as status-report language (`Suggested follow-up`, `Context read`, `Why I'm watching it`).
- Don't write bot identity, performance-judgment, or customer-impact prediction language.

## Output requirements

The final report must include:

- Timestamp and source status summary (Jira, Slack, Kepler, GitHub, Granola).
- Voice-tuned `My read` status summary in the operator register.
- One row/section per live-roster CSE, including zero-Jira members.
- Score from 1-10.
- Risk tier: Low, Moderate, Watch / approaching capacity, or At-risk.
- Confidence and missing-data notes.
- Jira load profile: raw open count, customer/team clusters, effective open load, actionable-now load, active delivery clusters, lineup/intake count, waiting/future-gated count, and duplicate same-customer clusters.
- Source-grouped drivers: Jira, customer communications, Slack/project, GitHub build, novel/internal work, Granola.
- Context assessment summary, counts, floor/cap, and advisory recommended band when investigator findings are provided.
- Saved artifact path, normally `cse-bandwidth-reports/YYYY-MM-DD.md`, plus the companion evidence path when context evidence was gathered.
- Suggested human follow-up for high or same-run approaching-capacity cases.

## Confidence rules

Four optional enrichment surfaces back the Jira baseline: Slack, Kepler, GitHub, Granola.

- High: Jira plus at least two enrichment surfaces checked successfully.
- Medium: Jira plus one enrichment surface checked successfully.
- Low: Jira only, or critical enrichment failures.

Missing optional surfaces reduce confidence and must be shown; they do not silently lower the score.

## Verification before reporting done

This skill is read-only and MCP/CLI-native; there is no test binary. Before reporting done, confirm:

1. A `jira_search` against `project = CSE` returns the open population.
2. A per-repo GitHub count returns for at least one resolved login: `gh api "repos/postman-cs/<repo>/commits?author=<login>&since=<ISO>" --jq '.[].sha' | wc -l`.
3. The SKILL.md names no retired path:

   ```sh
   ! grep -REn 'cse-bandwidth\.js|atlassian-rest|granola-rest|slack-rest|list_recent_communications|GRANOLA_API_KEY' \
     plugins/cse-tools/.agents/skills/cse-bandwidth/SKILL.md
   ```

4. The skill invoked no write tool (no Jira edit/transition/comment/create, no Slack post/update/delete/reaction, no `gh` PR/commit write). It never reassigns work. The report contains only reads and local report artifacts.
5. The org-wide `gh search commits` count was not used anywhere as a CSE's commit total.
