mcpbeat

Cowork Roi Report

microsoft/cowork-roi-report

| Generates a Microsoft-branded "What Cowork Did for Me" self-contained HTML impact report from the signed-in user's own Copilot Cowork session history in OneDrive. Quantifies leverage as a speed multiplier and a professional-services-equivalent value using research-anchored task-category bands and an artifact-scaled two-clock model, with a live hourly-rate control, KPIs, a work-by-business-process breakdown, a projects-by-category view, and an activity heatmap. Use when the user asks to "generate my impact summary report", "generate my impact report", "my impact summary", "my Cowork ROI", "what Cowork did for me", "Cowork impact report", "Copilot Cowork ROI report", "how much time has Cowork saved me", "my Cowork value report", or any request for a personal impact / ROI / value report on Copilot Cowork usage. or daily briefings.

73k tokens
context cost
the whole folder, loaded on every use
47
files
ships runnable scripts
0
copies elsewhere
how many repositories repackaged it
20
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/microsoft/What-I-did-with-Cowork --skill cowork-roi-report

What comes with it

242 545 bytes besides the instruction
CHANGELOG-v11.md
CHANGELOG-v13.md
CHANGELOG-v14.md
CHANGELOG-v15.md
CHANGELOG-v16.md
CHANGELOG-v18.md
CHANGELOG-v19.md
CHANGELOG-v20.md
CHANGELOG-v21.md
CHANGELOG-v22.md
CHANGELOG-v23.md
CHANGELOG-v24.md
CHANGELOG-v25.md
CHANGELOG-v26.md
CHANGELOG-v27.md
CHANGELOG-v28.md
CHANGELOG-v29.md
CHANGELOG-v30.md
CHANGELOG-v31.md
CHANGELOG-v32.md
CHANGELOG-v33.md
CHANGELOG-v34.md
CHANGELOG-v35.md
CHANGELOG-v37.md
CHANGELOG-v5.md
CHANGELOG-v6.md
README.md
examples/sample_sessions.json
references/classification-methodology.md
references/classification-reference.md
references/map-my-work-playbook.md
references/methodology.md
references/value-pillars.md
scripts/apqc_taxonomy.json
scripts/build_report.py
scripts/classify.py
scripts/compute.py
scripts/mine_session.py
scripts/process_overrides.example.json
scripts/process_overrides.json

The instruction itself

19 sections, as written by the author

Cowork ROI — Impact Report Generator

Builds a personal, shareable impact report from the user's own Copilot Cowork footprint. Self-service:

reads the signed-in user's Cowork session workspaces in OneDrive, classifies the work into the eight

methodology categories, applies research-anchored time-savings bands, and renders a Microsoft-branded HTML

web app; optionally automates itself and emails a digest. Generic — works for any user; no data hard-coded.

When to use

  • "What did Cowork do for me?" / "My Cowork ROI report" / "Cowork impact report"
  • "How much time has Copilot Cowork saved me this month?"
  • "Set up a monthly Cowork ROI email"

When NOT to use

  • GitHub Copilot (IDE/code) reports → microsoft/What-I-Did-Copilot run locally
  • Team announcements → stakeholder-comms; single-meeting recaps → meeting-intel; daily wrap-up → daily-briefing

Inputs & defaults

  • Period: the user picks 7, 15 or 30 days (asked at the start). Window = period ago 00:00 → today 23:59, user's local time zone.
  • Hourly rate: default $72/hr (blended professional-services rate; also editable live inside the report).
  • Copilot seat cost: not used — the report shows a speed multiplier + professional-services value, not an ROI ratio.
  • Default email recipient: the signed-in user themselves.

Workflow

1. Ask the user (one AskUserQuestion, two questions)

  • Q1 — Period: "Which period should the report measure?" → options Last 7 days, Last 15 days, Last 30 days.
  • Q2 — Delivery: "How do you want to run it?" → options:
  • Just run it once (generate the report now, no automation)
  • Run now & automate every N days, email me a digest (N matches the period: 7→every 7 days, 15→every 15, 30→every 30; each run emails the highlights with the HTML attached)

Do not schedule until the user explicitly chooses the automate option in Q2 (the platform also shows its own approval dialog).

2. Resolve identity & dates

  • GetMyDetails(select="mail,userPrincipalName,displayName") → user name + email.
  • after = N days ago 00:00 local; before = today 23:59 local; window.label = "Last N days", window.months = N/30 rounded (legacy).

3. Harvest the user's Cowork sessions (the data source)

Cowork persists each session's workspace to OneDrive under Documents/Cowork/ — the artifacts are the

signal. Harvest all three layouts (users/versions differ):

  • Task folders (current): Documents/Cowork/Tasks/<goal-slug>-<YYYY-MM-DD>/input/+output/
  • Root goal folders: Documents/Cowork/<goal-slug>-<YYYY-MM-DD>/input/+output/
  • Legacy UUID sessions: Documents/Cowork/sessions/<session-uuid>/input/+output/
  • GetDefaultDrive() → personal OneDrive drive_id.
  • Locate the Cowork folder — do NOT assume the name (may be Cowork 2, Colaborar, …). Try

/Documents/Cowork; on 404 list /Documents, pick the child starting Cowork (prefer exact, else highest N),

else /Cowork, else ask once. Carry the resolved name forward.

  • Enumerate all three layouts (Tasks/, root goal-folders, sessions/) and follow pagination to

exhaustion — a page caps at ~20–100 items.

  • Allow-list by app id. Count a folder/artifact ONLY when createdBy.application.id = the **Cowork app id

6ab48b67-cd74-4ad4-81af-5932984589be** — never key on folder *names*.

  • NEVER enumerate Documents/Apps/… — that tree is M365 Copilot "Scout" (Graph app 99fa64eb-…), not Cowork.
  • Keep session folders whose createdDateTime/lastModifiedDateTime falls in the window.
  • For each kept session, GetDriveChildren into output/ (and input/) to collect filenames, extensions and

per-file createdDateTime (parallel batches).

  • Counting discipline. Task folders accumulate artifacts over days — do NOT derive exec_min from

file-timestamp spans (leave null; prefer telemetry). Fold supporting files (screenshots, variant HTML,

READMEs, lock files) into the session's primary deliverable. Keep output-less sessions (empty outputs

classify.py tags general).

  • Live-session telemetry. mine_session.py --log …/cowork-session-telemetry.json logs each session's exec_min, tool intensity and artifacts. In the harvest, merge in any session id not covered by a Cowork folder (has_folder:false, outputs:[]); prefer telemetry exec_min where both exist. Forward-only.

4. Classify each session into run tasks (the methodology)

A session contains one or more run tasks; each run task maps to exactly one of the eight categories

below.

Value model = RUNS × BAND. Time saved = Σ over runs of each run's category band (minutes saved/run); each

band already sums the activity chain inside one run, so count runs × band — never per-LOC or per-artifact

add-ons. Count runs from tool-chains (code run ≈ 6 code-edits; analysis run ≈ 5 research calls); mine_session.py

writes a runs:{category:count} field. Without telemetry, estimate runs conservatively and label as estimates.

compute.py applies Σ runs × CATS[band].

Use the deterministic classifier — do NOT hand-tag categories. Write the harvested sessions (with

inputs, outputs and exec_min) to working/cowork_raw.json, then run:

python scripts/classify.py --in working/cowork_raw.json --out working/cowork_sessions.json --overrides working/process_overrides.json.

It maps each session's real artifact extensions to categories (e.g. .xlsx/.csv→analysis, .docx/.pptx/.pdf→document,

.html/.py/.ps1→code, .zip→special), caps ~2 run tasks/session, and tags output-less sessions general.

This is the fix for the failure mode where every session was stamped with the same category pair and every

goal collapsed to the same hours — never assign the same default categories to every session. You may bump

a clearly analytical deliverable (a synthesis report saved as .docx/.pptx) to analysis, but the extension

map is the default. Be conservative — credibility matters more than a big number.

Category choice follows the Cowork usage taxonomy (Description/Examples). classify.py is description-driven: Analysis & Research only from analytical goal text (synthesize, compare, brief from multiple sources), NOT a file type; a built spreadsheet is Document & content creation, not analysis. Email workflows (Outlook) and Communication workflows (Teams) are the same ideas on different surfaces. The 8 labels, 2-per-session cap, PRIORITY tie-break and document output-gate are unchanged; deterministic. Full rules: references/classification-methodology.md.

Extension→category heuristics, counting discipline, and the exact working/cowork_raw.json

schema classify.py consumes are in references/classification-reference.md.

Key rules: cap ~2 run tasks/session, fold supporting files into the primary task, and report

categories with no artifacts as zero (a conservative floor).

4a. Tag the skills behind each deliverable (populates "Skills augmented")

Required — else the Skills-augmented and Deliverables tables render empty. Tag each output (and each

chat-only session) with the professional skills Cowork exercised, ONLY from scripts/skills_vocabulary.json:

a skills:[...] array per outputs[] item (or session-level for chat-only). Also tag professional_roles:[...]

per session — the 1–2 roles a billing firm would charge (guide: scripts/roles_taxonomy.json) — driving the

"Roles Cowork assembled for me" section; if omitted, classify.py keyword-matches. **Tag conservatively from the

deliverable** (e.g. .pptx→*Presentation Design*; .docx→*Technical Writing*; skill .zip→*System Architecture*/

*Prompt Engineering*; .xlsx→*Data Analysis*) — never invent a skill outside the vocabulary. Past OneDrive-only

sessions are inferred, not measured — note that in the report.

4b. Align to the durable taxonomy memory, then derive process + JTBD (registry-first)

Business Process is the aggregation anchor; JTBD and Project nest under it. Process/Project names are

kept STABLE across runs by a durable taxonomy memory, so the model doesn't re-invent names each run.

The memory is PER-USER and never shared. The registry filename embeds a sanitized key from the user's

email (/mnt/user-config/.claude/cowork-process-registry.<userkey>.json) and carries an owner field on the

user's own mount. reconcile_taxonomy.py derives path+owner from meta.email (pass --owner) and **ignores

any registry whose owner ≠ the invoking user**, so a first run mints processes from the user's OWN sessions.

Nothing user-specific is committed to the skill folder; overrides are scratch under working/.

  • Reconcile first — align, create only if novel. After writing working/cowork_raw.json (step 3) and

BEFORE classify.py, run:

   python scripts/reconcile_taxonomy.py --in working/cowork_raw.json \
       --owner "<signed-in user's mail>" --overrides working/process_overrides.json

Per session it (a) matches a known Project and reuses {process,pillar,jtbd}; else (b) matches an

existing Process by keyword and registers a new project under it; else (c) mints a new Process

("new":true). Writes working/process_overrides.json and persists the owner-stamped registry.

  • Surface anything new (interactive runs). If the script prints NEW processes minted, tell the

user the new name(s) and offer to rename — edit the registry's processes/projects and re-run

reconcile_taxonomy.py. On unattended/scheduled runs it auto-creates the flagged entry and never

blocks. (On a genuine first run EVERY process is new — that is expected, not an error.)

  • classify.py then reads the overrides via --overrides working/process_overrides.json (each

session → {process, pillar, job, jtbd}; job is retained = the process name only for back-compat

with the not-yet-migrated member skill — it is not shown in this report). Pillars follow

references/value-pillars.md; the registry stores each process's pillar.

  • No-memory fallback: a first run builds the registry from processes discovered THIS run; if

reconcile_taxonomy.py can't run, classify.py falls back to scripts/apqc_taxonomy.json. Optional:

references/map-my-work-playbook.md to enrich novel process/JTBD naming.

The report's Work by business process section pivots on Process: each process is an accordion with

its subtotal (sessions · hours · value · % of time), the distinct JTBD(s) it served, and the

projects beneath it. A secondary By pillar toggle groups the same projects by value pillar.

(Report layout: a single "Your projects" table is the one place projects are listed, sitting under the KPI cards. A single Group-by dropdown (Process/Category) regroups rows in place, read once; rows render server-side (never blank). Columns: Project · Cowork-fit · Hours · Value. Each row carries a Cowork-fit dot (single-surface test): H green = build/automation, any Specialized workflow, or cross-surface; M yellow = borderline; L red = one surface. Two-layer hybrid: a deterministic rule sets the baseline, then an LLM review may confirm/adjust each grade (a keyword can’t truly judge capability). Each dot is flagged rule-based/AI-reviewed; hover shows reason + method. The Roles × projects heatmap replaces the flat role list under "Roles Cowork assembled for me"; only Deliverables stays behind a toggle.)

> Memory-first + packaging: each run locates the user's own owner-scoped registry and aligns to it (only

> novel work adds a name). NEVER bundle the registry, any cowork-process-registry*.json, or a populated

> process_overrides.json when sharing — overrides ship as {}. Personal processes leaking into another

> user's run is a fatal flaw the owner guard + working/ overrides path exist to prevent.

5. Compute & render (bundled scripts — no hand arithmetic)

  • python scripts/compute.py --in working/cowork_sessions.json --out working/cowork_roi_data.json
  • python scripts/build_report.py --data working/cowork_roi_data.json --out output/cowork-roi-report.html
  • Verify: Glob output/cowork-roi-report.html; if missing, locate + move into output/.
  • CSV export is OPT-IN (extra step) — don't generate by default; offer it in step 6 with the estimate (scripts/to_csv.py --estimate). On the user's yes: python scripts/to_csv.py --data working/cowork_roi_data.json --out output/cowork-sessions.csv (one row/session, atomic grain, pipe-delimited, UTF-8 BOM, Cowork-fit columns).
  • Run each pipeline script as its own command — never append an inline schema-guessing python -c (a wrong guess exits non-zero and marks the whole step Failed). Trust each script's printed summary.

6. Show highlights & verify

Present a short highlights summary (or render_ui card): speed multiplier, expert-equivalent hours, value, top 3 categories/goals. Tell the user the HTML report is saved. Then offer the optional session CSV with its estimated credit cost (scripts/to_csv.py --estimate; the script itself is ~0 credits — local compute — so the estimate is the one extra agent step). Generate only if the user opts in.

7. Automate (only if the user chose it in Q2)

SetupScheduledPrompt with execution_mode="inline", frequency Day, interval = N (7/15/30), hours

["8"], name "Cowork ROI report (every N days)", and a self-contained description restating the full

workflow (harvest last N days → classify → two-clock model at $72/hr → render HTML to output/ → email me the

highlights with the HTML attached). Confirm: "Done — I'll rebuild every N days and email the digest."

8. Email digest (if automating, or if asked to email it)

`SendEmailWithAttachments(to=[<user's own email>], subject="My Copilot Cowork impact — <window label>",

body="<highlights HTML>", content_type="HTML", direct_attachment_file_paths=["output/cowork-roi-report.html"])`.

Other recipient only if named.


Methodology (summary)

Each category's research-anchored Typical band (Low/High = range); an expert clock vs a modeled

assisted clock yields the speed multiplier; value = expert-hours × rate. No ROI/seat figure.

Bands table + sources + two-clock formulas: references/methodology.md.

compute.py holds the constants — never hand-compute.

Guardrails

  • No fabricated work. Every run task traces to a real session/artifact; zero-artifact categories show zero.
  • Conservative counting. Cap ~2 tasks/session; fold supporting files into the primary. Prefer credible over impressive.
  • No hand arithmetic. All numbers from compute.py.
  • Privacy. Show artifact filenames and short goal phrases only; never file contents.
  • Per-user memory — never leak it. The taxonomy registry is owner-scoped; reconcile_taxonomy.py

ignores any non-owner file. NEVER bundle the registry, any cowork-process-registry*.json, or a

populated process_overrides.json — overrides ship as {}, live under working/ at runtime.

  • Send/automate only on approval. Show the report first; schedule or email only after the user opts in (Q2).
  • Fail open. If the Cowork folder is missing/404, note it and ask for the folder name rather than aborting.

Bundled files

  • scripts/mine_session.py — telemetry: run time, tool intensity, artifacts per session.
  • scripts/reconcile_taxonomy.py — per-user taxonomy memory (align-first, create-if-novel); runs before classify.py; --owner.
  • scripts/classify.py — deterministic ext→category classifier; reads --overrides; emits compute.py’s input schema.
  • scripts/compute.py — research-anchored bands + two-clock model → payload JSON.
  • scripts/build_report.py — renders the single-file HTML (Process-anchored work-by-process, projects-by-category, glossary, live rate, PDF).
  • scripts/to_csv.pyopt-in tidy CSV export (one row/session, atomic grain, Cowork-fit columns); --estimate prints its credit cost (~0; local compute).

Durable files (outside the skill, persist across sessions — PER-USER, never bundled)

  • /mnt/user-config/.claude/cowork-process-registry.<userkey>.json — the user's own owner-stamped taxonomy memory (Processes + Projects + JTBDs); <userkey> from email, on the per-user mount. Read/aligned/persisted by reconcile_taxonomy.py each run; non-owner files ignored. Member/aggregated skills use the same scheme.
  • /mnt/user-config/.claude/cowork-session-telemetry.json · …-credits.json · …-session-costs.json — measured run-time / credit / cost logs (optional).

How to use it

Copy the folder

Take microsoft/cowork-roi-report from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.