---
name: executive_summary_writing
description: "Write an executive summary of an existing long document: read for decisions and findings, fix a length budget, and write a standalone summary that carries the main conclusion, the key evidence, the costs or risks, and the ask or next step, with every number traced to the source and no new claims added. Use when a finished report, study, proposal, or other long document needs a short summary that stands on its own."
metadata: { "includeInPrompt": true }
---

# Executive Summary Writing

## Purpose

Take a finished long document and write a short summary that a reader can act on without reading the full text. The summary reports what the source document says. It carries the main conclusion, the key evidence behind that conclusion, the costs or risks the source names, and the ask or next step the source states. It adds nothing of its own: no new numbers, no new findings, and no recommendation beyond what the source itself states.

Scope:
- executive-decision-memos covers arguing for a decision with options and a recommendation. An executive summary is different: it reports what a finished document says and adds no recommendation of its own beyond what the source states.
- annual-report-writing covers a yearly organization report, written as a complete account of a year. This skill is narrower: it summarizes one existing document, whatever its subject or period.
- white-paper-writing covers authoring an evidence document from scratch. This skill does no authoring from scratch. The source document already exists, and the work is selection, compression, and faithful reporting of that source.
- This skill owns reading a source for decisions and findings, fixing a length budget, extracting the conclusion, evidence, costs or risks, and ask, drafting a standalone summary, and checking every number in the draft against the source.
- This skill does not cover writing the source document, deciding the question the source should answer, or making the decision the source informs. If the source is unfinished or its conclusion is not stated, stop and report that gap instead of supplying a conclusion.

Verification note: the claim check script in the Operating Rules section was run on 2026-10-16 against a fixture source and a fixture summary built into the script. On that fixture it reported summary_words 76, word_budget 100, words_over_budget 0, numbers_in_summary 14, numbers_supported 13, numbers_unsupported 1, and unsupported_numbers 500000, and it exited with code 1 because the planted 500000 figure appears in the summary but not in the source. A cleaned summary with that figure corrected passed with exit code 0 in a second run on the same day.

## Workflow

1. Confirm the source is finished and identify it exactly.
   - Record the source title, its date or version as given in the document, and its length in pages or sections as stated in the document itself. Work from that version only. If a newer version appears mid task, restart the extraction in step 3 from the newer version.
   - Read the source in full at least once before drafting. A summary built from headings, an introduction, and a closing section alone will miss qualifications, costs, and risks that sit in the middle sections.
   - Check that the source states a conclusion. If it presents findings without drawing a conclusion, the summary reports the findings and states plainly that the source draws no conclusion. Do not draw one on its behalf.

2. Fix a length budget before drafting, then hold it.
   - Take the budget from the request when one is given: a word count, a page count, or a named format such as a summary box in a template. When no budget is given, set one before drafting and write it at the top of the working file, so cuts in step 7 have a target.
   - State the budget as a word count even when it was given as pages, because the check script in the Operating Rules section works in words. Convert once, note the conversion in the working file, and use the word budget from then on.
   - Treat the budget as a limit, not a target to fill. A summary that covers the required parts under budget is finished. Padding to reach the budget adds claims the source did not prioritize.

3. Read for decisions and findings, and build an extraction list.
   - Mark every passage where the source states a decision, a finding, a result, or a change from a prior state. Note the section or page for each passage so a later check can find it again fast.
   - Mark every passage that gives evidence for a finding: counts, measurements, comparisons, dates, and named records or methods. Keep the evidence note attached to the finding it supports, not in a separate pile.
   - Mark every passage that names a cost, a risk, a limit, or an uncertainty. Include costs and risks the source states in money, in time, in scope, or in words alone.
   - Mark the ask or next step exactly as the source states it: what is requested, who is asked where the source names a party, and any date or condition the source attaches. If the source states no ask, mark that as a fact to report, not a gap to fill.

4. Rank the extraction list and cut it to the budget.
   - Put the main conclusion first. The main conclusion is the one the source itself puts first, repeats, or builds its closing around. If two conclusions compete, report both in the order the source gives them and do not merge them into a single claim the source never made.
   - Keep the evidence that carries the conclusion: the strongest result, the comparison that shows change, and the base count or scope that makes the result readable. Cut background, method detail beyond what proves the result, and findings the source treats as minor.
   - Keep at least one cost or risk item whenever the source names any. A summary that carries only good results misreports a source that named costs or risks.
   - Keep the ask or next step whole. Cutting its date or condition to save words changes what is being asked.

5. Draft the summary as a standalone text.
   - Order the draft in this sequence unless the request sets a different order: scope in one line, main conclusion, key evidence, costs or risks, ask or next step. A reader who stops after the conclusion line still gets the source position right.
   - Write the scope line from the extraction list: what the source covers, over what period, and on what base, using only figures the source states. On the fixture in the Operating Rules section, the scope figures were 12 regional offices, 9 months, 47 staff interviewed, and 320 case files.
   - Write each evidence sentence with its number and its unit or base together. A rate without its base, or a total without its period, cannot be checked and reads as larger or smaller than the source meant.
   - Write costs and risks in the source terms. If the source gives a cost figure, give it as the source gives it. If the source names a risk in words, report it in close words and do not convert it into a number.
   - Write the ask exactly as extracted in step 3, with its date or condition attached. If the source states no ask, close with a plain line that the source states no ask or next step.

6. Trace every number and every claim to the source.
   - List each number in the draft in order, and next to it the passage in the source where that number appears. A number with no passage gets cut or corrected before any style pass.
   - Check units, periods, and bases as well as values. A correct value with the wrong period, or a percent taken from one base and applied to another, is a wrong number.
   - Check each non numeric claim the same way: name the passage that supports it. Watch for strengthened wording in particular: a source that says a result suggests a change does not support a summary that says it proves the change.
   - Cut any claim that rests only on implication. If the source implies a point but never states it, the summary leaves it out. The summary reports the document, not a reading of it.

7. Run the claim check, fix what it flags, and cut to budget.
   - Save the script from the Operating Rules section as claim_check.py, save the source as a plain text file, save the draft summary as a plain text file, and run the script with the word budget from step 2.
   - On its fixture the script reports: summary_words 76, word_budget 100, words_over_budget 0, numbers_in_summary 14, numbers_supported 13, numbers_unsupported 1, and unsupported_numbers 500000. The fixture is built so that one planted figure fails. On a real draft, any unsupported figure means the draft is wrong or the number was copied with an error: find it in the source by hand, fix the draft, or cut the figure, then run the script again.
   - If words_over_budget is above 0, cut in this order: background first, minor findings next, method detail next, and repeated evidence last. Do not cut the conclusion, the base figures, the cost or risk line, or the ask.
   - Note the script limit plainly: it checks that a number appears in the source. It does not check that the number is used with the right unit, period, or meaning. The trace pass in step 6 covers that, and the script does not replace it.

8. Do a standalone read and a final check.
   - Read the summary alone, without the source open, and mark any sentence that needs the source to make sense: an undefined term, a comparison with no base, a pronoun with no clear referent. Fix each mark inside the summary itself.
   - Read the summary against the source once more, in order, and confirm the emphasis matches: the point the summary leads with is the point the source leads with, and no minor source point has grown larger in the summary than it is in the source.
   - Confirm the final word count with a fresh script run and record its output with the delivered summary. The recorded run is the evidence that the delivered text, not an earlier draft, passed the check.

## Output Contract

Deliver the executive summary plus its trace record, in this order:

1. Source identification: source title, date or version as given in the source, the word budget used, and the final word count from the last script run. On the worked fixture in the Operating Rules section those check figures are summary_words 76 against word_budget 100.
2. The summary itself, as a standalone text, in the step 5 order: scope line, main conclusion, key evidence, costs or risks, and ask or next step, or a plain line that the source states no ask.
3. Number trace: every number in the summary in order, with the source passage, section, or page that supports it. Numbers that were cut after a failed check are listed as cut, with the reason.
4. Claim trace for non numeric claims: each load bearing claim in the summary with the source passage that supports it. Claims with no passage do not appear in the summary and are listed here as cut if they were drafted.
5. Check record: the final claim_check.py output for the delivered summary, showing summary_words, word_budget, words_over_budget, numbers_in_summary, numbers_supported, and numbers_unsupported. A delivered summary has numbers_unsupported 0 and words_over_budget 0.

## Operating Rules

1. Report the source, do not improve it. The summary carries the source conclusion, evidence, costs or risks, and ask as stated. Plainer wording is fine. Stronger wording, added reasons, and added recommendations are not.
2. No new claims. Every number and every load bearing claim traces to a named passage in the source. If a point cannot be traced, it is cut, however reasonable it sounds.
3. Numbers keep their unit, period, and base. A figure moved to a different unit, period, or base than the source used is treated as an unsupported figure and fails the check in step 6.
4. Costs and risks stay in. When the source names a cost, a risk, a limit, or an uncertainty, the summary names it too, in proportion to how the source treats it. Good results alone are a misreport.
5. The budget governs length. The word budget fixed in step 2 is the limit for the delivered summary. Background and minor findings are cut before any required part is shortened.
6. The source order governs emphasis. The summary leads with what the source leads with and closes with the source ask. Reordering to make a weaker point look central is a misreport even when every sentence is individually supported.
7. Absence is reported, not filled. If the source states no conclusion, no cost, or no ask, the summary says so in plain words. It never supplies the missing part.
8. Plain language, source terms for key terms. Use short sentences and the source own term for any defined concept. Do not rename a source concept into a broader or narrower term.
9. Use the claim check script below on every draft before delivery. Save it as claim_check.py, run it as its header describes, and keep its final output in the check record. Report figures from its output, not from a hand count.

### Claim check (claim_check.py)

Run: `python3 claim_check.py --make-fixture` writes the worked example files source.txt and summary.txt in the current folder. Run: `python3 claim_check.py source.txt summary.txt --budget 100` checks a real source and summary against a word budget of 100. Change the budget value to the budget fixed in step 2. On the fixture it exits with code 1, because the summary contains the planted figure 500000, which the source does not state.

Expected output for the fixture run:

```text
number: 12: supported
number: 9: supported
number: 47: supported
number: 320: supported
number: 14.2: supported
number: 8.6: supported
number: 1240000: supported
number: 68: supported
number: 3.4: supported
number: 1.9: supported
number: 81: supported
number: 210000: supported
number: 6: supported
number: 500000: unsupported
summary_words: 76
word_budget: 100
words_over_budget: 0
numbers_in_summary: 14
numbers_supported: 13
numbers_unsupported: 1
unsupported_numbers: 500000
```

The fixture is built to trip the unsupported check once: the source states response time falling from 14.2 days to 8.6 days, operating cost 1240000, an error rate falling from 3.4 percent to 1.9 percent, satisfaction at 81 percent, and a next phase budget of 210000 over 6 months, and it states no savings total. The summary copies those figures and adds savings of 500000, which is the one unsupported figure.

claim_check.py (Python 3, standard library only). Save this code as claim_check.py:

```python
#!/usr/bin/env python3
"""Check numbers in an executive summary against a source document. Python 3, stdlib only.

Usage:
  python3 claim_check.py source.txt summary.txt [--budget 100]
  python3 claim_check.py --make-fixture

The script extracts every number from the summary, normalises it
(commas removed, trailing decimal zeros removed), and checks whether
that number appears in the source document. It also counts summary
words against the word budget. Exit code is 1 when any summary number
is unsupported or the summary is over budget, else 0.
"""
import re
import sys

SOURCE_FIXTURE = """Regional Service Review: Full Report. This report covers 12 regional offices reviewed over 9 months. The review team interviewed 47 staff members and examined 320 case files. Average response time fell from 14.2 days at the start of the period to 8.6 days at the end. Operating cost for the period was 1240000. Staffing accounted for 68 percent of operating cost. The error rate was 3.4 percent in the first quarter and 1.9 percent in the final quarter. Satisfaction was reported by 81 percent of respondents. The report states a next phase budget of 210000 and a next phase timeline of 6 months. No savings total is stated in this report.
"""

SUMMARY_FIXTURE = """This review covered 12 regional offices over 9 months, based on interviews with 47 staff and a review of 320 case files. Response time improved from 14.2 days to 8.6 days. Operating cost was 1240000, with staffing at 68 percent of that total. Error rate fell from 3.4 percent to 1.9 percent, and 81 percent of respondents reported satisfaction. The report states a next phase budget of 210000 over 6 months, and estimates savings of 500000.
"""

NUMBER_RE = re.compile(r"\d{1,3}(?:,\d{3})+(?:\.\d+)?|\d+(?:\.\d+)?")


def canonical(token):
    text = token.replace(",", "").rstrip(".")
    if "." in text:
        text = text.rstrip("0").rstrip(".")
    if "." not in text:
        text = text.lstrip("0") or "0"
    return text


def numbers_in(text):
    found = []
    for match in NUMBER_RE.finditer(text):
        token = match.group(0).rstrip(".")
        if token:
            found.append((token, canonical(token)))
    return found


def main(argv):
    if "--make-fixture" in argv:
        with open("source.txt", "w", encoding="utf-8") as f:
            f.write(SOURCE_FIXTURE)
        with open("summary.txt", "w", encoding="utf-8") as f:
            f.write(SUMMARY_FIXTURE)
        print("wrote source.txt and summary.txt")
        return 0
    args = [a for a in argv[1:] if not a.startswith("--budget")]
    budget = 200
    if "--budget" in argv:
        idx = argv.index("--budget")
        try:
            budget = int(argv[idx + 1])
        except (IndexError, ValueError):
            print("usage: python3 claim_check.py source.txt summary.txt [--budget N]")
            return 2
        args = [a for a in argv[1:] if a != "--budget" and a != argv[idx + 1]]
    if len(args) != 2:
        print("usage: python3 claim_check.py source.txt summary.txt [--budget N]")
        return 2
    with open(args[0], encoding="utf-8") as f:
        source_text = f.read()
    with open(args[1], encoding="utf-8") as f:
        summary_text = f.read()
    source_numbers = {canon for _tok, canon in numbers_in(source_text)}
    summary_numbers = numbers_in(summary_text)
    unsupported = []
    supported = 0
    for token, canon in summary_numbers:
        if canon in source_numbers:
            supported += 1
            print(f"number: {token}: supported")
        else:
            unsupported.append(token)
            print(f"number: {token}: unsupported")
    words = len(summary_text.split())
    over = max(0, words - budget)
    print(f"summary_words: {words}")
    print(f"word_budget: {budget}")
    print(f"words_over_budget: {over}")
    print(f"numbers_in_summary: {len(summary_numbers)}")
    print(f"numbers_supported: {supported}")
    print(f"numbers_unsupported: {len(unsupported)}")
    print(f"unsupported_numbers: {', '.join(unsupported) if unsupported else '-'}")
    return 1 if (unsupported or over) else 0


if __name__ == "__main__":
    raise SystemExit(main(sys.argv))
```
