Felix Reyes

Companion repository

The Skills Pack

Turning Finance Work into Tested AI Skills

The book, skill packs and supporting files in one place.

Felix Reyes · Grain Press · Draft, 5 October 2026

The Skills Pack draft book cover

Downloads

Skill packs

Browse the 16 skills included in the companion. Open a skill for its inputs, outputs, folder structure and source instructions.

Variance & Bridge

v2.0.0

Explain performance against budget, prior year and forecast. Start here for a focused variance review.

Budget vs actualCompare monthly and year-to-date performance with budget, then identify the movements that matter.
Inputs
P&L CSV: period, line item, Revenue/COGS/Opex category, actual and budget.
Outputs
  • bva.json
  • bva-note.md
Folder structure
budget-vs-actual/
├── scripts/
│   └── bva.py
└── SKILL.md
Read SKILL.md

packs/variance-bridge/skills/budget-vs-actual/SKILL.md

---
name: budget-vs-actual
description: Compares actual to budget by P&L line for a month and year to date, with favourable and unfavourable variances, and writes the variance commentary. Use when asked for budget vs actual, BvA, actual versus plan, a P&L variance analysis, why we missed or beat budget, or commentary on the month or the year to date. Needs a P&L CSV with period, line item, category, actual and budget.
---

# Budget vs actual

Produces two files: the script's figures (`bva.json`) and a variance
commentary note (`bva-note.md`). Unless the user names other paths, write
both to an `output/` folder in the current working directory.

## Steps

1. Find the P&L CSV the user named. Open only its header row. It needs
   `period` (YYYY-MM), `line_item`, `category` (Revenue, COGS or Opex),
   `actual` and `budget`. If any are missing, ask.
2. Run the script, `skills/budget-vs-actual/scripts/bva.py`. As in every
   file in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/budget-vs-actual/scripts/bva.py \
           --input <the CSV> --output output/bva.json

   The month defaults to the last period in the file; the year to date runs
   from the first month of that year to the month. If the user names another
   month, add `--period YYYY-MM`.
3. If the script exits with an error, here or in step 4, show it and stop.
   Exit code 3 means the variances do not reconcile; never report its figures.
4. Run it with `--note`; it writes the note, Drivers left as a marked line:

       python3 <pack root>/skills/budget-vs-actual/scripts/bva.py --note \
           --input output/bva.json --output output/bva-note.md

5. Replace the marked line with the Drivers bullets, to the contract below,
   using only `display` in `output/bva.json`. Never edit another section.
6. Check the note before you finish. Every figure must be a display string,
   character for character, with parentheses exactly where `display` has
   them: a negative keeps them in prose too ("a variance of (0.9) pts") and
   a positive figure never gets them. Join a percentage to its amount with
   "or": "favourable by $38K, or 28.5%".

## Reading the display block

- A positive variance is favourable: revenue or profit above budget, or a
  cost below it. An unfavourable variance prints in parentheses. Use only
  the words favourable and unfavourable for the sign.
- `display.ytd` and `display.month` hold every line and the subtotals.
- `display.movers` ranks the cost lines (COGS and Opex) by the size of their
  year-to-date variance: up to three favourable and three unfavourable.
  Revenue is not ranked; `display.monthly_revenue` shows its variance month
  by month.
- `display.peaks` gives each line's largest year-to-date variance and the
  month it was reached. A line whose year-to-date variance is now much
  smaller than its peak reversed during the year.
- Every amount is in $K, rounded half up from its exact figure on its own:
  $11,238K, ($362K). One that is not zero but would show as zero keeps the
  fewest decimals that show it: $0.4K. `display.rounding` names the rows
  whose rounded figures do not add up, and is empty when they all do.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence: year-to-date revenue against budget and operating income
    against budget, each with its variance and variance percentage, from
    `display.ytd.subtotals`.

    ## Results
    A table with columns Measure, Actual, Budget and Variance, and these
    rows in this order, from `display.ytd.subtotals`: Revenue, COGS, Gross
    profit, Gross margin, Operating expenses, Operating income. The Gross
    margin row shows percentages, and its Variance is the points string,
    which ends in pts.

    ## Month
    One to three sentences on the month's revenue and operating income
    against budget, from `display.month.subtotals`. The first sentence
    starts with `display.labels.period_name`, month and year.

    ## Drivers
    Two to four bullets. Each names one line from `display.movers` with its
    year-to-date variance; or the months in `display.monthly_revenue` where
    the revenue miss concentrated, with their variances; or a line whose
    `display.peaks` entry shows a variance that reversed, with the peak and
    its month.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`
    Then display.rounding, after a blank line, when it is not empty.

- Copy every figure exactly as it appears in `display`, including parentheses.
- Display figures are rounded. Never add them up, subtract them or derive
  new figures from them, and never add up months.
- You choose which movers matter, their order, and what to ask each line
  owner. Call a variance timing only when `display.monthly_revenue` or
  `display.peaks` shows the reversal. Do not name a cause the data does not
  show.
- The Results table's COGS row is the COGS category: Cost of goods sold
  plus Freight out. In prose, name a line by its `line_item` in full
  ("Cost of goods sold"), and keep "COGS" for the category row.
Revenue bridgeExplain the revenue change between two years through customer movements.
Inputs
Customer CSV: ID, name, prior-year and current-year revenue; timing amount is optional.
Outputs
  • bridge.json
  • bridge-note.md
Folder structure
revenue-bridge/
├── references/
│   └── definitions.md
├── scripts/
│   └── bridge.py
└── SKILL.md
Read SKILL.md

packs/variance-bridge/skills/revenue-bridge/SKILL.md

---
name: revenue-bridge
description: Computes a prior-year to current-year revenue bridge by customer (timing, loss, decline, upsell, new) and writes a one-page commentary note. Use when asked for a revenue bridge, a PY-to-CY revenue walk, a customer bridge, why revenue changed year over year, or which customers drove the change. Needs a customer CSV with prior-year and current-year revenue columns.
---

# Revenue bridge

Produces two files: the script's figures (`bridge.json`) and a commentary
note (`bridge-note.md`). Unless the user names other paths, write both to an
`output/` folder in the current working directory.

## Steps

1. Find the input CSV the user named. Open only its header row. It needs
   `customer_id`, `customer_name`, a prior-year and a current-year revenue
   column, and optionally `timing_amount`. If any are missing, ask.
2. Run the script, `skills/revenue-bridge/scripts/bridge.py`. As in every
   file in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/revenue-bridge/scripts/bridge.py \
           --input <the CSV> --output output/bridge.json

   If the revenue columns are not named `py_revenue`/`cy_revenue` or
   `fyNNNN_revenue`, add `--py-col <name> --cy-col <name>`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the bridge does not reconcile; never report its figures. Before
   exit 0 the script checks that PY + Timing + Loss + Decline + Upsell + New
   = CY to the cent.
4. Read `output/bridge.json`. Use only the strings under `display`.
5. Write `output/bridge-note.md` to the contract below.
6. Check the note before you finish. Every figure must be a display string,
   character for character, with parentheses exactly where `display` has
   them: a negative keeps them and a positive figure never gets them. Join a
   figure to a name with a verb: "Ember Analytics added $136K".

## Output contract

The note has exactly these four sections, in this order:

    ## Headline
    One sentence: current-year revenue, prior-year revenue, and the change
    with its percentage (the script prints n/a when prior-year revenue is 0).

    ## Bridge
    A table with columns Step and Amount, and these rows in this order:
    <PY label>, Timing, Loss, Decline, Upsell, New, <CY label>.

    ## Drivers
    Two to four bullets. Each names one customer from `display.movers` and
    joins its amount to the name with a verb ("Ember Analytics added
    $136K"), or gives one movement total with its customer count.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`
    If display.rounding is not empty, copy it whole on the next line.

- Copy every figure exactly as it appears in `display`, including parentheses.
- Display figures are rounded. Never add them up or derive new figures from them.
- Use the labels from `labels` (for example FY2024) for the first and last rows.
- You choose which customers and movements to name and in what order. Do not
  name a cause the data does not show.
- See `skills/revenue-bridge/references/definitions.md` for what each movement
  means and how the display rounds.
Price, volume & mixSeparate a revenue change into price, volume and mix effects by product family and segment.
Inputs
Product CSV: product family, segment, units and revenue for both years.
Outputs
  • pvm.json
  • pvm-note.md
Folder structure
price-volume-mix/
├── references/
│   └── method.md
├── scripts/
│   └── pvm.py
└── SKILL.md
Read SKILL.md

packs/variance-bridge/skills/price-volume-mix/SKILL.md

---
name: price-volume-mix
description: Splits a revenue change between two years into price, volume and mix effects by product family and segment, reconciled to the cent, and writes the commentary. Use when asked for a price-volume-mix analysis, PVM, rate-volume-mix, how much of the revenue change was price versus volume, or a mix shift explanation. Needs a product CSV with units and revenue for both years.
---

# Price-volume-mix

Produces two files: the script's figures (`pvm.json`) and a commentary note
(`pvm-note.md`). Unless the user names other paths, write both to an
`output/` folder in the current working directory.

## Steps

1. Find the product CSV the user named. Open only its header row. It needs
   `product_family`, `segment`, and units and revenue for both years. If
   any are missing, ask.
2. Run the script, `skills/price-volume-mix/scripts/pvm.py`. As in every
   file in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/price-volume-mix/scripts/pvm.py \
           --input <the CSV> --output output/pvm.json

   By default the script uses the two `fyNNNN_units` and `fyNNNN_revenue`
   column pairs, the earlier year as prior year. If the columns are named
   otherwise, add `--py-units`, `--py-rev`, `--cy-units` and `--cy-rev`,
   each with a column name.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the effects do not reconcile; never report its figures. Before
   exit 0 the script checks that PY + Volume + Mix + Price = CY to the
   cent, in total and for every family and segment, and that the families
   and the segments add up to the totals.
4. Read `output/pvm.json`. Use only the strings under `display`.
5. Write `output/pvm-note.md` to the contract below.
6. Check the note before you finish. Every figure must be a display string,
   character for character, with parentheses exactly where `display` has
   them: a negative keeps them and a positive figure never gets them. Join a
   figure to a name with a verb: "Paper Mid-Market added $75K".

See `skills/price-volume-mix/references/method.md` for what each effect
means, how it is rounded and how the display rounds.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence: current-year revenue against prior-year revenue, and the
    change with its percentage, from `display.totals`.

    ## Bridge
    A table with columns Step and Amount, and these rows in this order,
    from `display.totals`: <PY label>, Volume, Mix, Price, <CY label>.

    ## By family
    A table with columns Family, <PY label>, <CY label>, Change, Price, Mix
    and Volume: one row per family, in the order of `display.by_family`.

    ## Drivers
    Two to four bullets. Each bullet starts with the name of a family or a
    segment, from `display.by_family`, `display.by_segment` or
    `display.movers`, and gives its effect. The totals are in the Headline
    and Bridge; no bullet restates them.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`
    If display.rounding is not empty, copy it whole on the next line.

- Copy every figure exactly as it appears in `display`, including parentheses.
- Display figures are rounded. Never add them up or derive new figures from them.
- Use the labels from `display.labels` (for example FY2024) for the first
  and last bridge rows and the year columns.
- You decide whether the story is price or volume, which family carries it,
  and what a pricing or sales reader would ask next. Do not compute a
  price, an average price or a share of the total. Call a change a mix
  shift only when `display.by_segment` or `display.movers.mix` shows it.
Forecast accuracyCompare forecast versions with actual results and identify where the forecasting process missed.
Inputs
Forecast-versions CSV plus the P&L actuals and budget. Forecast rows identify closed months.
Outputs
  • accuracy.json
  • accuracy-note.md
Folder structure
forecast-accuracy/
├── references/
│   └── metrics.md
├── scripts/
│   └── accuracy.py
└── SKILL.md
Read SKILL.md

packs/variance-bridge/skills/forecast-accuracy/SKILL.md

---
name: forecast-accuracy
description: Measures how accurate each forecast version was against actuals (full-year variance, mean absolute percentage error, bias, worst month) and writes the accuracy review. Use when asked how good the forecasts or reforecasts were, forecast accuracy, forecast versus actual by version, MAPE, bias, or which reforecast to trust. Needs a forecast-versions CSV and the P&L actuals.
---

# Forecast accuracy

Produces two files: the script's figures (`accuracy.json`) and an accuracy
review (`accuracy-note.md`). Unless the user names other paths, write both
to an `output/` folder in the current working directory.

## Steps

1. Find the forecast-versions CSV and the P&L CSV the user named. Open only
   their header rows. The forecasts need `version`, `period`, `line_item`,
   `amount` and `is_actual` (1 for a closed month that carries the actual,
   0 for a forecast month); the P&L needs `period`, `line_item`, `category`,
   `actual` and `budget`. If a file or a column is missing, ask.
2. Run the script, `skills/forecast-accuracy/scripts/accuracy.py`. As in
   every file in this pack, paths are relative to the pack root, the top
   folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/forecast-accuracy/scripts/accuracy.py \
           --forecasts <the forecasts CSV> --actuals <the P&L CSV> \
           --output output/accuracy.json

   A forecast line takes its actuals from the P&L line of the same name;
   Total operating expenses is the sum of the P&L's Opex lines.
3. If the script exits with an error, show the error and stop. Exit code 3
   means a version does not tie to the P&L; never report its figures.
   Before exit 0 the script checks that every closed month carries the P&L
   actual and that the Budget version equals the P&L budget.
4. Read `output/accuracy.json`. Use only the strings under `display`.
5. Write `output/accuracy-note.md` to the contract below.
6. Check the note before you finish. Every figure must be a display string,
   character for character, with parentheses exactly where `display` has
   them: a negative keeps them and a positive figure never gets them. Join a
   percentage to its amount with "or": "a variance of $3K, or 0.02%".

See `skills/forecast-accuracy/references/metrics.md` for what each metric
means, what a reader may conclude from it and how the display rounds.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence: the most accurate revenue version by name, from
    `display.best`, with its full-year variance and MAPE.

    ## Revenue
    A table with columns Version, Closed months, FY forecast, FY actual,
    Variance, %, MAPE and Bias: one row per version for Revenue, in the
    order of `display.labels.versions`. The cells are the version's
    `closed_months`, `fy_forecast`, `fy_actual`, `fy_variance`,
    `fy_variance_pct`, `mape` and `bias` from `display.versions`.

    ## Operating expenses
    The same table for Total operating expenses.

    ## Findings
    Two to four bullets. Each bullet starts with a version name from
    `display.labels.versions` and gives a figure for that version from
    `display.versions`, such as its worst month or its MAPE.

    ## Data notes
    One bullet per input, in this form, with the backticks, where <file>,
    <rows> and <sha12> are the strings in display.inputs.forecasts, then
    display.inputs.actuals:
    - <file>, <rows> rows, sha256 `<sha12>`
    If display.rounding is not empty, copy it whole as one more bullet.

- Copy every figure exactly as it appears in `display`, including parentheses.
- Display figures are rounded. Never add them up or derive new figures from them.
- You judge whether accuracy improved because fewer months were left to
  forecast or because the forecasting improved, what a single large miss
  means for the process, and which version a planner should anchor on. Do
  not compute a difference between two versions' figures, average MAPEs, or
  call a miss a one-off without naming its month.
Pack routing instructions

packs/variance-bridge/SKILL.md

---
name: variance-bridge
description: Builds revenue bridges, budget-vs-actual commentary, price-volume-mix analyses and forecast-accuracy reviews for finance reviews. Use when asked for a revenue bridge or PY-to-CY walk, why revenue changed, budget vs actual or variance commentary, price versus volume versus mix, or how accurate the forecasts were. Not for monthly reporting packs, account reconciliations or forward forecasts.
license: MIT
compatibility: Requires Python 3.9 or later (standard library only) and permission to run scripts. If scripts cannot run, the pack stops and says so; it never computes figures itself.
metadata:
  pack-version: "2.0.0"
  pack-standard: "skills-pack/1.0"
  house: "interstationary/2.0.0"
---

# Variance and Bridge pack

This pack holds several skills. Pick the row that matches the request, read
every file listed for it, then follow the skill. Read the files before
answering; do not answer from memory.

All paths in this pack, in every file, are relative to this file's folder
(the pack root).

| Request | Read first | Then read |
| --- | --- | --- |
| Revenue bridge, PY-to-CY walk, customer movement, why revenue changed year over year | `skills/revenue-bridge/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Budget vs actual, BvA, actual versus plan, P&L variance commentary for a month or the year | `skills/budget-vs-actual/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Price-volume-mix, PVM, how much of the change was price versus volume, mix shift | `skills/price-volume-mix/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Forecast accuracy, how good were the reforecasts, forecast versus actual by version, MAPE, bias | `skills/forecast-accuracy/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Anything else (monthly review, close, cash, scenarios) | Not in this pack | Say so and stop |

## Rules for every skill in this pack

1. Numbers come from scripts. Never calculate, round or restate a figure yourself.
2. If a script fails or cannot run, report the error message and stop.
3. If the request is missing an input the skill needs, ask for it. Do not guess.

Management Reporting

v2.0.0

Turn a common set of P&L and KPI data into a scorecard, monthly narrative, executive summary and presentation.

KPI scorecardReview actuals against KPI targets and identify persistent exceptions.
Inputs
KPI CSV with targets, units, direction and definitions, plus the P&L CSV.
Outputs
  • scorecard.json
  • scorecard-note.md
Folder structure
kpi-scorecard/
├── scripts/
│   └── scorecard.py
└── SKILL.md
Read SKILL.md

packs/management-reporting/skills/kpi-scorecard/SKILL.md

---
name: kpi-scorecard
description: "Builds the monthly KPI scorecard: each KPI against target, variance, status, and months off target, tied to the P&L, and writes the scorecard note. Use when asked for the KPI scorecard, KPI dashboard, metrics versus target, which KPIs are off target, or a KPI exceptions list for a month. Needs the KPI CSV and the P&L CSV."
---

# KPI scorecard

Produces two files: the script's figures (`output/scorecard.json`) and the
scorecard note (`output/scorecard-note.md`). Unless the user names other
paths, write both to an `output/` folder in the current working directory.

## Steps

1. Find the KPI CSV and the P&L CSV the user named. Open only their header
   rows. The KPI file needs `period`, `kpi_id`, `kpi_name`, `unit`,
   `actual`, `target`, `direction` and `definition`; the P&L file needs
   `period`, `line_item`, `category`, `actual` and `budget`. If a file or a
   column is missing, ask.
2. Run the script. As in every file in this pack, paths are relative to the
   pack root, the top folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/kpi-scorecard/scripts/scorecard.py \
           --kpis <the KPI CSV> --pnl <the P&L CSV> \
           --output output/scorecard.json

   For a month other than the last one in the KPI file, add
   `--period YYYY-MM`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the scorecard does not tie to the P&L; never report its figures.
4. Read `output/scorecard.json`. Use only the strings under `display`.
5. Write `output/scorecard-note.md` to the contract below.

## What the script does

The script is `skills/kpi-scorecard/scripts/scorecard.py`. For each KPI in
the month:

- Variance is favourable-positive: actual minus target when `direction` is
  `higher`, target minus actual when it is `lower`. Status is `on target`
  when the variance is zero or more, else `off target`.
- Months off (`streak`) counts the consecutive months, ending with this one,
  that the KPI was off target; it is 0 when the KPI is on target.
- Percent KPIs print as percentages; their variance and month-on-month
  change are percentage points and end in pts: `(1.7) pts` means 1.7 points
  worse than target. Days print with one decimal, turns with two, counts as
  whole numbers.
- `exceptions` lists the KPIs off target three months or more; when there
  are none, it lists every KPI off target.
- Each figure is rounded on its own. `rounding` names the KPIs whose
  variance, as shown, is not the gap between actual and target as shown,
  and is empty when there are none.

Before it exits 0, the script checks three ties to the P&L for the month:
the revenue KPI equals P&L revenue; gross margin percent equals revenue less
the COGS lines, over revenue, to four places; opex percent equals the Opex
lines over revenue, to four places. Flags and defaults: `--period` the last
month in the KPI file; `--revenue-kpi revenue`, `--gm-kpi
gross_margin_pct` and `--opex-kpi opex_pct_revenue` name the tied KPIs.

## Output contract

The note has exactly these four sections, in this order. Text in angle
brackets is a string from `display`, copied whole.

    ## Headline
    One sentence, with the counts as figures:
    <counts.on_target> of <counts.kpis> KPIs were on target in
    <labels.period_name>, and <counts.streak_3_plus> have been off target
    for three months or more.

    ## Scorecard
    A table with columns KPI, Actual, Target, Variance, Status, Months off.
    One row per KPI in display.kpis, in that order, with the cells kpi_name,
    actual, target, variance, status and streak.

    ## Exceptions
    One bullet per KPI in display.exceptions, at most four. Each starts
    with this sentence and ends with the question to put to the KPI's owner:
    - <kpi_name>: <actual> against a target of <target>; months off
      target: <streak>.
    If display.exceptions is empty, write the one bullet
    - No KPI was off target.

    ## Data notes
    One bullet per input, in this form, with the backticks, where <file>,
    <rows> and <sha12> are the strings in display.inputs.kpis, then
    display.inputs.pnl:
    - <file>, <rows> rows, sha256 `<sha12>`
    If display.rounding is not empty, copy it whole as one more bullet.

- Copy every figure exactly as `display` prints it, parentheses included,
  and every KPI name whole: write "(0.3) pts", never "0.3 pts". Keep
  the figures in the template sentence; the question to the owner reads
  better without them. Never add or subtract two figures.
- Use a KPI's `definition` to say what it measures.
- Never recompute a KPI from the P&L, invent a target, or call a one-month
  miss a trend.
Monthly business reviewProduce the written financial narrative for a monthly business review.
Inputs
P&L CSV and KPI CSV for the reporting period.
Outputs
  • mbr.json
  • mbr-narrative.md
Folder structure
mbr-narrative/
├── scripts/
│   └── mbr.py
└── SKILL.md
Read SKILL.md

packs/management-reporting/skills/mbr-narrative/SKILL.md

---
name: mbr-narrative
description: "Writes the monthly business review narrative: month and year-to-date P&L against budget, the largest line variances and the KPI exceptions, from script figures. Use when asked for the MBR narrative, monthly business review commentary, the monthly operating review write-up, or what happened this month. Needs the P&L CSV and the KPI CSV."
---

# MBR narrative

Produces two files: the script's figures (`output/mbr.json`) and the
narrative (`output/mbr-narrative.md`). Unless the user names other paths,
write both to an `output/` folder in the current working directory.

## Steps

1. Find the P&L CSV and the KPI CSV the user named. Open only their header
   rows. The P&L file needs `period`, `line_item`, `category`, `actual` and
   `budget`; the KPI file needs `period`, `kpi_id`, `kpi_name`, `unit`,
   `actual`, `target`, `direction` and `definition`. If a file or a column
   is missing, ask.
2. Run the script. As in every file in this pack, paths are relative to the
   pack root, the top folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/mbr-narrative/scripts/mbr.py \
           --pnl <the P&L CSV> --kpis <the KPI CSV> --output output/mbr.json

   For a month other than the last one in the P&L, add `--period YYYY-MM`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the figures do not reconcile; never report them.
4. Read `output/mbr.json`. Use only the strings under `display`.
5. Write `output/mbr-narrative.md` to the contract below.

## What the script does

The script is `skills/mbr-narrative/scripts/mbr.py`.

- Month is the period; year to date runs from January to the period.
- Variances are favourable-positive: actual minus budget for revenue and
  profit, budget minus actual for costs. Gross margin variance is in points.
- `line_variances` holds, for the month and for the year to date, the three
  P&L lines with the largest variance by size, each marked `favourable` or
  `unfavourable`.
- `prior_month` holds the prior month's revenue and the change from it: the
  only month-on-month figures there are.
- `kpi_exceptions` lists the KPIs off target this month, longest streak of
  months off target first.
- Money prints in $K, each amount rounded on its own (company-branding's
  Numbers). `month.rounding` and `ytd.rounding` name the rows and figures
  that do not add up as shown, and are empty when they all do.

Before it exits 0, the script checks that operating income built from the
lines equals revenue less COGS less opex, for the month and the year to
date; that the months' revenue sums to year-to-date revenue; and that the
revenue KPI equals P&L revenue for the month. Flags and defaults: `--period`
the last month in the P&L; `--revenue-kpi revenue`.

## Output contract

The narrative has exactly these six sections, in this order. Text in angle
brackets is a string from `display`, copied whole.

    ## Headline
    One sentence, from display.month.subtotals:
    <labels.period_name> revenue was <revenue.actual> against a budget of
    <revenue.budget>, and operating income was <operating_income.actual>
    against <operating_income.budget>.

    ## Month
    A table with columns Measure, Actual, Budget, Variance and these rows in
    this order: Revenue, Gross profit, Gross margin, Operating expenses,
    Operating income, from display.month.subtotals keys revenue,
    gross_profit, gross_margin_pct, opex and operating_income.
    Write each label exactly as listed. The Gross margin row shows
    percentages, and its Variance is the points string, which ends in pts.

    ## Year to date
    The same table from display.ytd.subtotals.

    ## Drivers
    Two to four bullets, each on one entry of display.line_variances.month
    or display.line_variances.ytd, starting with its name:
    - <line_item>: a variance of <variance> in the month (or for the year
      to date), <actual> against <budget>.
    A bullet may add one sentence that links it to another display figure.

    ## KPIs
    One to four bullets, one per KPI in display.kpi_exceptions:
    - <kpi_name>: <actual> against a target of <target>; months off
      target: <streak>.
    If display.kpi_exceptions is empty, write the one bullet
    - No KPI was off target.

    ## Data notes
    One bullet per input, in this form, with the backticks, where <file>,
    <rows> and <sha12> are the strings in display.inputs.pnl, then
    display.inputs.kpis:
    - <file>, <rows> rows, sha256 `<sha12>`
    Then display.month.rounding and display.ytd.rounding, each copied whole
    as one more bullet when it is not empty.

- Copy every figure exactly as `display` prints it, parentheses included,
  even where the sentence already says "below" or "over": write "fell
  ($12K) short" and "(0.3) pts", never "fell $12K short" or "0.3
  pts". Keep the figures in the template sentence; an added sentence
  reads better without them. Never add, subtract or re-round two figures.
- Tell the month first, then say whether the year to date confirms it. Lead
  the drivers with the line variances that matter most.
- Never compute a month-on-month change other than `prior_month`, name a
  cause the data does not show, or restate the tables in prose.
Executive summaryCondense the financial result into a brief for the CFO, board or leadership team.
Inputs
P&L CSV and KPI CSV.
Outputs
  • summary.json
  • exec-summary.md
Folder structure
exec-summary/
├── scripts/
│   └── summary.py
└── SKILL.md
Read SKILL.md

packs/management-reporting/skills/exec-summary/SKILL.md

---
name: exec-summary
description: "Writes a one-page executive summary of the year to date for the CFO or the board: results against budget, quarterly trend, three drivers and a watch list. Use when asked for an executive summary, a one-pager, a board or CFO summary, a year-to-date summary, or the headlines for leadership. Needs the P&L CSV and the KPI CSV."
---

# Executive summary

Produces two files: the script's figures (`output/summary.json`) and the
one-page summary (`output/exec-summary.md`). Unless the user names other
paths, write both to an `output/` folder in the current working directory.

## Steps

1. Find the P&L CSV and the KPI CSV the user named. Open only their header
   rows. The P&L file needs `period`, `line_item`, `category`, `actual` and
   `budget`; the KPI file needs `period`, `kpi_id`, `kpi_name`, `unit`,
   `actual`, `target`, `direction` and `definition`. If a file or a column
   is missing, ask.
2. Run the script. As in every file in this pack, paths are relative to the
   pack root, the top folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/exec-summary/scripts/summary.py \
           --pnl <the P&L CSV> --kpis <the KPI CSV> --output output/summary.json

   For a year to date that ends before the last month in the P&L, add
   `--period YYYY-MM`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the figures do not reconcile; never report them.
4. Read `output/summary.json`. Use only the strings under `display`.
5. Write `output/exec-summary.md` to the contract below.

## What the script does

The script is `skills/exec-summary/scripts/summary.py`.

- The year to date runs from January to the period; `display.labels.ytd`
  names it (`FY2025` for a full year).
- `quarters` holds each quarter's revenue against budget, for the months up
  to the period; a quarter not yet complete is labelled, for example,
  `Q4 to date`.
- Variances are favourable-positive: actual minus budget for revenue and
  profit, budget minus actual for costs.
- `movers` holds the three most favourable and the three most unfavourable
  P&L lines for the year to date, each with its `direction`.
- `kpis.exceptions` lists the KPIs off target in the period's month, longest
  streak first.
- Money prints in $K, each amount rounded on its own (company-branding's
  Numbers). `rounding` names the rows that do not add up as shown, and is
  empty when they all do.

Before it exits 0, the script checks that the quarters' revenue sums to
year-to-date revenue, that operating income built from the lines equals the
subtotal, and that the revenue KPI equals P&L revenue for the month. Flags
and defaults: `--period` the last month in the P&L; `--revenue-kpi revenue`.

## Output contract

The summary has exactly these six sections, in this order, and at most 350
words in all, tables included. Text in angle brackets is a string from
`display`, copied whole.

    ## Headline
    One sentence, from display.ytd.subtotals:
    <labels.ytd> revenue was <revenue.actual> against a budget of
    <revenue.budget>, and operating income was <operating_income.actual>
    against <operating_income.budget>.

    ## Results
    A table with columns Measure, Actual, Budget, Variance and these rows in
    this order: Revenue, Gross profit, Gross margin, Operating expenses,
    Operating income, from display.ytd.subtotals keys revenue, gross_profit,
    gross_margin_pct, opex and operating_income.
    Write each label exactly as listed. The Gross margin row shows
    percentages, and its Variance is the points string, which ends in pts.

    ## Quarterly revenue
    A table with columns Quarter, Actual, Budget, Variance: one row per
    entry in display.quarters, with quarter, actual, budget and variance.

    ## Drivers
    Two to four bullets, each on one entry of display.movers, starting:
    - <line_item>: <variance>, <direction> for the year.
    A bullet may add one sentence on what it means for the board.

    ## Watch list
    One to four bullets, one per KPI you keep from display.kpis.exceptions:
    - <kpi_name>: <actual> against a target of <target>; months off
      target: <streak>.
    Then one sentence: the question leadership should put to the business.

    ## Data notes
    One bullet per input, in this form, with the backticks, where <file>,
    <rows> and <sha12> are the strings in display.inputs.pnl, then
    display.inputs.kpis:
    - <file>, <rows> rows, sha256 `<sha12>`
    If display.rounding is not empty, copy it whole as one more bullet.

- Copy every figure exactly as `display` prints it, parentheses included,
  even where the sentence already says "below" or "over": write "fell
  ($12K) short" and "(0.3) pts", never "fell $12K short" or "0.3
  pts". Keep the figures in the template sentence; an added sentence
  reads better without them. Never add, subtract or re-round two figures,
  even ones that sit side by side.
- Put the three things a board member must know first, in the order they
  would ask. Leave a KPI off the watch list when it does not need their
  attention.
- Never forecast, annualize, compare with a prior year (it is not in the
  inputs), name a cause the data does not show, or run past one page.
Review deckBuild a six-slide monthly review from the scorecard and MBR calculation outputs.
Inputs
scorecard.json and mbr.json in the required schemas, plus the pack’s brand configuration.
Outputs
  • slide-map.json
  • deck.json
  • IS_MBR_<period>.pptx
Folder structure
deck-assembly/
├── references/
│   └── slide-map.md
├── scripts/
│   └── build_deck.py
└── SKILL.md
Read SKILL.md

packs/management-reporting/skills/deck-assembly/SKILL.md

---
name: deck-assembly
description: Assembles the monthly business review deck as a .pptx from a slide map and the house brand, copying every figure from the scorecard and narrative outputs. Use when asked to build, assemble or produce the MBR deck, the monthly review slides, the board deck pages for the month, or to turn the scorecard and narrative into slides. Needs the scorecard and narrative JSON outputs.
compatibility: Requires Python 3.9 or later, permission to run scripts, and the python-pptx package (pip install python-pptx).
---

# Deck assembly

You write the slide map (`output/slide-map.json`); the script checks it and
builds the deck. It writes `output/deck.json` and the deck, named by the
house brand: `output/IS_MBR_<period>.pptx`. Unless the user names other
paths, all three go to an `output/` folder in the current working directory.

## Steps

1. Find the two result files the user named: the KPI scorecard
   (`output/scorecard.json`, schema kpi-scorecard/2) and the MBR figures
   (`output/mbr.json`, schema mbr-narrative/2). If either is missing, say
   which and ask the user to produce it first with the KPI scorecard or MBR
   narrative skill. Never write either file yourself.
2. Read `skills/deck-assembly/references/slide-map.md`, then the `display`
   block of both files.
3. Write `output/slide-map.json` to that reference: six slides with fixed
   titles, layouts and tables; you choose the bullets.
4. Run the script. As in every file in this pack, paths are relative to the
   pack root, the top folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/deck-assembly/scripts/build_deck.py \
           --slide-map output/slide-map.json --output output/deck.json

5. Exit code 2 that names a slide, or exit code 3: the slide map you wrote
   breaks a rule, and `output/deck.json` lists any figure that is in neither
   source. Correct the slide map and run the script once more. Any other
   error (python-pptx missing, a source or brand key missing), or a second
   failure: show the error and stop. No deck is written on exit 3.
6. Reply with the deck's path and the line the script printed.

## What the script does

The script is `skills/deck-assembly/scripts/build_deck.py`. It checks the
slide map's schema, the six titles and layouts, the table columns, at most
12 rows a table, the bullet counts, and at most 140 characters a bullet.
The two tables must equal the sources' display rows, and every Drivers and
Watch list bullet must name a line or a KPI from the MBR display block.
Then it finds every figure in every title, subtitle, bullet and cell (a
token that starts with a digit, `$`, a sign or a parenthesis) and requires
each to be a display string of one of the two sources, or a figure inside
one. That check is its identity: any figure from elsewhere is exit 3.

Under the P&L table it prints the MBR's `display.month.rounding`, when not
empty, beside the source line.

It reads the house brand from
`_shared/company-branding/assets/brand.json`: colours, fonts, the footer
and the deck's file name. `--brand <path>` names another brand file;
`--pptx <path>` names another deck path.

## Output contract

The deliverable is `output/slide-map.json`, written exactly as
`skills/deck-assembly/references/slide-map.md` sets out. The deck and
`output/deck.json` are the script's.

- Copy every figure exactly as a source's `display` block prints it,
  parentheses included. Never type, add or round a figure.
- Choose the two to four headline bullets that carry the month, the drivers
  that earn a line, and the wording, in the house voice and under 140
  characters a bullet.
- Never change a title, add or drop a slide, reorder the KPIs, or pick a
  colour or font; the brand file decides those.
Pack routing instructions

packs/management-reporting/SKILL.md

---
name: management-reporting
description: "Produces the monthly business review: KPI scorecard, MBR narrative, executive summary and the assembled deck, all from the same P&L and KPI files. Use when asked for the monthly review, MBR, KPI scorecard, executive summary, one-pager for the CFO or board, or the review deck. Not for variance bridges, close reviews or forecasts."
license: MIT
compatibility: Requires Python 3.9 or later and permission to run scripts. Scripts use the standard library, except deck-assembly, which needs the python-pptx package (pip install python-pptx). If scripts cannot run, the pack stops and says so; it never computes figures itself.
metadata:
  pack-version: "2.0.0"
  pack-standard: "skills-pack/1.0"
  house: "interstationary/2.0.0"
---

# Management Reporting pack

This pack holds several skills. Pick the row that matches the request, read
every file listed for it, then follow the skill. Read the files before
answering; do not answer from memory.

All paths in this pack, in every file, are relative to this file's folder
(the pack root).

| Request | Read first | Then read |
| --- | --- | --- |
| KPI scorecard, KPI dashboard, metrics versus target, which KPIs are off target | `skills/kpi-scorecard/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md`, `_shared/company-branding/SKILL.md` |
| MBR narrative, monthly business review commentary, what happened this month | `skills/mbr-narrative/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md`, `_shared/company-branding/SKILL.md` |
| Executive summary, one-pager, board or CFO summary, year-to-date headlines | `skills/exec-summary/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md`, `_shared/company-branding/SKILL.md` |
| Build or assemble the MBR deck, review slides, turn the scorecard and narrative into slides | `skills/deck-assembly/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md`, `_shared/company-branding/SKILL.md` |
| Anything else (bridges, variance analysis, close, forecasts) | Not in this pack | Say so and stop |

## Rules for every skill in this pack

1. Numbers come from scripts. Never calculate, round or restate a figure yourself.
2. If a script fails or cannot run, report the error message and stop.
3. If the request is missing an input the skill needs, ask for it. Do not guess.

Close & Reconciliation

v1.1.0

Review the close process and the evidence behind balances. Identify exceptions and the people who need to resolve them.

Close checklistIdentify late tasks, dependency breaks and missing evidence in the month-end close.
Inputs
Checklist CSV: task, owner, area, due and completed workdays, status, dependencies and evidence reference.
Outputs
  • checklist.json
  • checklist-note.md
Folder structure
close-checklist/
├── scripts/
│   └── checklist.py
└── SKILL.md
Read SKILL.md

packs/close-reconciliation/skills/close-checklist/SKILL.md

---
name: close-checklist
description: "Reviews the close checklist: late tasks, dependency order breaks, missing evidence and the critical-path delay, by area and owner, and writes the close retrospective. Use when asked how the close went, close checklist status, late close tasks, close timeline review, or what to fix for next close. Needs the close checklist CSV."
---

# Close checklist review

Produces two files: the script's figures (`output/checklist.json`) and the
close retrospective (`output/checklist-note.md`). Unless the user names
other paths, write both to an `output/` folder in the current working
directory. Write the note in the house voice
(`_shared/executive-voice/SKILL.md`) and trace every figure
(`_shared/source-trace/SKILL.md`).

## Steps

1. Find the checklist CSV the user named. Open only its header row. It
   needs `task_id`, `task`, `owner`, `area`, `workday_due`, `workday_done`,
   `status`, `depends_on` and `evidence_ref`. If any are missing, ask.
2. Run the script, `skills/close-checklist/scripts/checklist.py`. As in
   every file in this pack, paths are relative to the pack root, the top
   folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/close-checklist/scripts/checklist.py \
           --input <the CSV> --output output/checklist.json

   The defaults are `--as-of 6` (the close workday of the snapshot) and
   `--period` taken from the file name (`YYYY-MM`). Change one only when the
   user names another.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the counts do not add up; never report its figures.
4. Read `output/checklist.json`. Use only the strings under `display`.
5. Write `output/checklist-note.md` to the contract below.

## What the script decides

A task is late when it finished after its due workday, or is still open
after it. The slip is the days late. A dependency violation is a task
completed before a task it depends on finished, or while that task is open.
Missing evidence is a complete task with no `evidence_ref`. The critical
path runs back from the task due last (the reporting package) through the
dependency that finished last at each step; `display.critical_path.first_late`
is the first late task on it, the one that pushed the end task. Before it
exits 0 the script checks the identity: complete plus open equals tasks, and
the areas' counts add up to the totals.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence in this form, each <...> a display string:
    The close ran <counts.tasks> tasks with <counts.late> late, and
    <critical_path.end_task> finished <critical_path.slip> days late
    (write "day" when the slip is 1).

    ## Late tasks
    A table with the columns ID, Task, Owner, Area, Due, Done and Slip. One
    row per entry in display.late, in that order; the cells are its
    task_id, task, owner, area, due, done and slip.

    ## Exceptions
    One bullet per entry in display.violations, then one per entry in
    display.missing_evidence. Each names the task ID and its owner. When
    both lists are empty, one bullet that says so.

    ## Next close
    Two to four bullets. Each is an action for a named owner from
    display.labels.owners: what to re-sequence, start earlier or document.
    Start from the critical path.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`

- Every figure is a display string, copied exactly. Write counts as
  digits. Workdays print as `WD+2`; write them that way. Write no figure,
  date or code that `display` does not hold.
- Parentheses mean a negative figure. Put nothing else in parentheses: write
  "CL-17 slipped 2 days", never "CL-17 slipped (2)".
- You contribute which late task caused the reporting delay and which were
  merely late, what to re-sequence, and who needs a conversation. Never add
  or drop a late task or an exception, compute a slip, move a due date, or
  mark a task complete.
Account reconciliation reviewReview account reconciliations against policy and identify what blocks sign-off.
Inputs
Three CSVs: account reconciliations, reconciling items and trial balance.
Outputs
  • recs.json
  • recs-note.md
Folder structure
account-rec-review/
├── references/
│   └── rec-policy.md
├── scripts/
│   └── rec_review.py
└── SKILL.md
Read SKILL.md

packs/close-reconciliation/skills/account-rec-review/SKILL.md

---
name: account-rec-review
description: "Reviews account reconciliations against policy: unexplained differences, aged items, preparer equals reviewer, late review, stale GL balances, and writes the sign-off note. Use when asked to review the recs, account reconciliation review, rec sign-off, open reconciling items, or which recs block the close. Needs the recs CSV, the rec items CSV and the trial balance."
---

# Account reconciliation review

Produces two files: the script's figures (`output/recs.json`) and the
sign-off note (`output/recs-note.md`). Unless the user names other paths,
write both to an `output/` folder in the current working directory. Write
the note in the house voice (`_shared/executive-voice/SKILL.md`) and trace
every figure (`_shared/source-trace/SKILL.md`).

## Steps

1. Find the three CSVs the user named: the recs (one row per account), the
   reconciling items, and the trial balance. If one is missing, ask.
2. Run the script, `skills/account-rec-review/scripts/rec_review.py`. As in
   every file in this pack, paths are relative to the pack root, the top
   folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/account-rec-review/scripts/rec_review.py \
           --recs <recs CSV> --items <items CSV> --tb <trial balance CSV> \
           --output output/recs.json

   The defaults are `--max-unexplained 5000`, `--max-age 90` (days),
   `--review-by 5` (close workday) and `--tb-column dec_2025`. Change one
   only when the user names another threshold or period.
3. If the script exits with an error, show the error and stop. Exit code 3
   means a rec does not tie; never report its figures.
4. Read `output/recs.json`. Use only the strings under `display`.
5. Read `skills/account-rec-review/references/rec-policy.md`, then write
   `output/recs-note.md` to the contract below.

## What the script decides

Each rec gets a tag for every rule it breaks: `stale GL balance` (the GL
balance on the rec differs from the trial balance), `unexplained over
threshold` (the unexplained difference is above `--max-unexplained`),
`aged item` (a reconciling item is older than `--max-age` days), `preparer
is reviewer`, and `late review` (no review, or a review after
`--review-by`). A rec with no tag is clean. Before it exits 0 the script
checks the identity on every rec: the items sum to the reconciling items
total, and GL balance minus subledger balance minus reconciling items equals
the unexplained difference.

## Output contract

The note has exactly these four sections, in this order:

    ## Headline
    One sentence in this form, each <...> a display string:
    <counts.clean> of <counts.recs> recs are clean, and the unexplained
    difference across all recs nets to <totals.unexplained>.

    ## Findings
    A table with the columns Account, Name, Finding, Unexplained, Preparer,
    Reviewer and Reviewed. One row per entry in display.findings, in the
    order display lists them (largest unexplained difference first); do not
    re-sort the rows. The cells are its account, account_name, tags,
    unexplained_difference, preparer, reviewer and reviewed_workday.

    ## Sign-off
    Two to five bullets, most severe first, in the policy's order (this
    order is for the bullets only; the table keeps display order).
    Each starts with the account name and a colon, says what must happen
    before sign-off, and names the person to ask by name: the finding's
    preparer or reviewer, or the Controller, as display.labels.people
    prints them.

    ## Data notes
    One bullet per input, in this form, with the backticks, where <file>,
    <rows> and <sha12> are the strings in display.inputs.recs,
    display.inputs.items, then display.inputs.tb:
    - <file>, <rows> rows, sha256 `<sha12>`

- Every figure is a display string, copied exactly. Write counts as
  digits. Write no figure, date or code that `display` does not hold.
- Display figures are rounded. Never add them up or derive new figures.
- Parentheses mean a negative figure. Keep them exactly as `display`
  prints them and use them for nothing else; set an account number or an
  aside off with commas: "Inventory, account 1200" and "RI-05, ($14K),
  is 127 days old".
- You contribute the severity order, whether a finding is a policy breach
  or a bookkeeping gap, and what the preparer or reviewer is asked to do.
  Never add or drop a finding, compute an age, a difference or a total,
  change a status, or treat an item as cleared.
Accrual reviewReview accrual support, age and movement before deciding what to release, true up or hold.
Inputs
Accruals CSV with owners, support and two months of amounts, plus the trial balance.
Outputs
  • accruals.json
  • accruals-note.md
Folder structure
accrual-review/
├── scripts/
│   └── accruals.py
└── SKILL.md
Read SKILL.md

packs/close-reconciliation/skills/accrual-review/SKILL.md

---
name: accrual-review
description: "Reviews month-end accruals: ties them to the trial balance, flags long-open, unsupported, swinging and double-count-risk accruals, and writes the release-or-true-up note. Use when asked to review accruals, accrual reasonableness, which accruals to release or true up, stale accruals, or accrual support before close. Needs the accruals CSV and the trial balance."
---

# Accrual review

Produces two files: the script's figures (`output/accruals.json`) and the
release-or-true-up note (`output/accruals-note.md`). Unless the user names
other paths, write both to an `output/` folder in the current working
directory. Write the note in the house voice
(`_shared/executive-voice/SKILL.md`) and trace every figure
(`_shared/source-trace/SKILL.md`).

## Steps

1. Find the accruals CSV and the trial balance CSV the user named. The
   accruals file needs `accrual_id`, `account`, `vendor`, `description`,
   `basis`, `support_ref`, `reverses`, `owner`, `months_open`,
   `invoice_received`, `note` and two `amount_YYYY_MM` columns. If one is
   missing, ask.
2. Run the script, `skills/accrual-review/scripts/accruals.py`. As in every
   file in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/accrual-review/scripts/accruals.py \
           --accruals <accruals CSV> --tb <trial balance CSV> \
           --output output/accruals.json

   The defaults are `--max-months 3`, `--swing 0.50`,
   `--tb-column dec_2025` and `--tb-prior-column nov_2025`. Change one only
   when the user names another threshold or period.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the accruals do not tie to the trial balance; never report their
   figures.
4. Read `output/accruals.json`. Use only the strings under `display`.
5. Write `output/accruals-note.md` to the contract below.

## What the script decides

Each accrual gets a tag for every rule it breaks: `open over 3 months`
(`months_open` above `--max-months`), `no support` (blank `support_ref`),
`swing over 50.0%` (it moved more than `--swing` of last month's amount),
`new accrual` (nothing last month), and `invoice received` (the invoice is
already in payables, so the cost may be booked twice). A swing or a new
accrual is a finding only when the preparer left the `note` blank; the ones
with a note are listed under `display.explained` and are not findings, so
leave them out of the note. Before it exits 0 the script checks the
identity: for each account, this month's accruals sum to minus the trial
balance, and last month's to minus last month's trial balance.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence in this form, each <...> a display string:
    <totals.current> is accrued across <counts.accruals> accruals at
    <labels.current>, and <counts.accruals_with_findings> of them have
    findings.

    ## Findings
    A table with the columns ID, Vendor, <labels.current>,
    <labels.prior>, Change, Months open, Basis and Finding. One row
    per entry in display.findings, in that order; the cells are its
    accrual_id, vendor, amount_current, amount_prior, swing_pct,
    months_open, basis and tags.

    ## By basis
    A table with the columns Basis, Amount, Count and Share. One row per
    entry in display.totals.by_basis, in that order.

    ## Release or true up
    Two to five bullets that together cover every finding. Each names the
    accrual ID and its owner and asks the owner one question: release it,
    true it up, or hold it, and what support to send.

    ## Data notes
    One bullet per input, in this form, with the backticks, where <file>,
    <rows> and <sha12> are the strings in display.inputs.accruals, then
    display.inputs.tb:
    - <file>, <rows> rows, sha256 `<sha12>`

How to ask: an accrual open for months may no longer be owed (release it,
or get the vendor's confirmation); one with no support needs the document
or a release; a swing or a new accrual without a note needs the owner's
explanation or a true-up; an accrual whose invoice was received is
released, because payables already holds the cost.

- Every figure is a display string, copied exactly. Write counts as
  digits. Write no figure, date or code that `display` does not hold.
- Display figures are rounded. Never add them up or derive new figures.
- Parentheses mean a negative figure. Keep them exactly as `display`
  prints them, and put nothing else in parentheses, no account number and
  no positive figure: write "AC-05 rose 133.6%", never "AC-05 (133.6%)".
- You contribute the recommendation for each finding, the support to ask
  for, and whether the estimate share looks high. Never add or drop a
  finding, compute a change or a share, release an accrual yourself, or
  name an invoice the data does not show.
Balance sheet fluxReview account movements against explicit dollar and percentage thresholds.
Inputs
Trial-balance CSV with account metadata and a column for each balance date.
Outputs
  • flux.json
  • flux-note.md
Folder structure
flux-analysis/
├── references/
│   └── review-questions.md
├── scripts/
│   └── flux.py
└── SKILL.md
Read SKILL.md

packs/close-reconciliation/skills/flux-analysis/SKILL.md

---
name: flux-analysis
description: "Runs the month-end flux: balance movements against the prior period, flagged by percent and dollar thresholds, with sign flips and likely reclasses marked, and writes the flux review. Use when asked for a flux analysis, balance sheet flux, account movement review, variance review of the trial balance, or what moved this month in the TB. Needs the trial balance CSV."
---

# Flux analysis

Produces two files: the script's figures (`output/flux.json`) and the flux
review (`output/flux-note.md`). Unless the user names other paths, write
both to an `output/` folder in the current working directory. Write the
review in the house voice (`_shared/executive-voice/SKILL.md`) and trace
every figure (`_shared/source-trace/SKILL.md`).

## Steps

1. Find the trial balance CSV the user named. Open only its header row. It
   needs `account`, `account_name`, `account_type`, `normal_balance`,
   `pnl_line` and one column per balance date. If any are missing, ask.
2. Run the script, `skills/flux-analysis/scripts/flux.py`. As in every file
   in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/flux-analysis/scripts/flux.py \
           --input <the CSV> --output output/flux.json

   The defaults are `--current dec_2025`, `--compare nov_2025`,
   `--pct 0.10`, `--amount 50000` and `--types Asset,Liability,Equity`.
   Change one only when the user names another period or threshold.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the trial balance does not sum to zero; never report its figures.
4. Read `output/flux.json`. Use only the strings under `display`.
5. Read `skills/flux-analysis/references/review-questions.md`, then write
   `output/flux-note.md` to the contract below.

## What the script decides

`movement` is the current balance minus the comparison balance. An account
whose `account_type` is in `--types` is flagged when its movement is above
`--amount` and above `--pct` of the comparison balance; a comparison balance
of zero counts as above `--pct`. The script tags `sign flip` when the two
balances have opposite signs, and `possible reclass with <account>` when
another balance-sheet account moved by exactly the opposite amount.
Movements above `--pct` but not above `--amount` are listed under
`display.below_threshold`. Before it exits 0 the script checks the identity:
every balance column sums to zero.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence in this form, each <...> a display string:
    <counts.flagged> of <counts.balance_sheet> balance-sheet accounts moved
    more than <thresholds.pct> and more than <thresholds.amount> from
    <labels.compare> to <labels.current>.

    ## Flagged movements
    A table with the columns Account, Name, <labels.compare>,
    <labels.current>, Movement, % and Tags. One row per entry in
    display.flags, in that order; the cells are its account, account_name,
    compare, current, movement, movement_pct and tags.

    ## Review
    One bullet per flagged account, in table order, with no sub-bullets.
    Start with the account name, then its movement in this form: moved
    <movement>, <movement_pct>. Say which reading the data supports: a
    business reason, put as a question for the owner to confirm; a reclass
    with its pair; a likely error, and why; or a sign flip. End with whom
    to ask.

    ## Below threshold
    Zero to three bullets, each about one entry in display.below_threshold,
    in this form: <account_name> moved <movement>, <movement_pct>. One short
    sentence may follow.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`

- Every figure is a display string, copied exactly. Write counts as
  digits. Write no figure, date or code that `display` does not hold.
- Display figures are rounded. Never add them up or derive new figures.
- Parentheses mean a negative figure. Keep them exactly as `display`
  prints them, even where your words say a balance grew: a liability that
  grew prints negative. Put nothing else in parentheses, no account number
  and no positive figure: write "Inventory rose $210K, or 17.3%", never
  "Inventory (1200) rose $210K (17.3%)".
- You contribute the reading of each flag and the question for its owner.
  Never flag an account the script did not flag, drop one it did, or state
  a cause the data does not show.
Pack routing instructions

packs/close-reconciliation/SKILL.md

---
name: close-reconciliation
description: "Reviews the month-end close: trial balance flux, account reconciliations, accruals and the close checklist, flagging what needs a look before sign-off. Use when asked for a flux analysis, rec review, accrual review, close status or close retrospective. Not for management reporting, variance commentary or forecasts."
license: MIT
compatibility: Requires Python 3.9 or later (standard library only) and permission to run scripts. If scripts cannot run, the pack stops and says so; it never computes figures itself.
metadata:
  pack-version: "1.1.0"
  pack-standard: "skills-pack/1.0"
  house: "interstationary/1.0.0"
---

# Close reconciliation pack

This pack holds four skills. Pick the row that matches the request, read
every file listed for it, then follow the skill. Read the files before
answering; do not answer from memory.

All paths in this pack, in every file, are relative to this file's folder
(the pack root).

| Request | Read first | Then read |
| --- | --- | --- |
| Flux analysis, balance sheet flux, what moved in the trial balance, account movement review | `skills/flux-analysis/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Review the recs, account reconciliation review, rec sign-off, open reconciling items | `skills/account-rec-review/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Review the accruals, accrual reasonableness, release or true up, accrual support | `skills/accrual-review/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| How the close went, close checklist status, late tasks, close retrospective | `skills/close-checklist/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Anything else (P&L variances, KPIs, forecasts) | Not in this pack | Say so and stop |

## Rules for every skill in this pack

1. Numbers come from scripts. Never calculate, round or restate a figure yourself.
2. If a script fails or cannot run, report the error message and stop.
   Exit code 3 means an identity failed: never report those figures.
3. If the request is missing an input the skill needs, ask for it. Do not guess.
4. The script decides what is flagged. Never add, drop or soften a flag, and
   never change a status, an owner, an amount or a date.

Forecast & Planning

v1.1.0

Review forecast drivers, weekly liquidity, scenarios and the assumptions behind the plan.

Forecast driver reviewExplain the change between two forecast versions through an ordered EBITDA walk.
Inputs
Drivers CSV: version, period, driver and value.
Outputs
  • drivers.json
  • drivers-note.md
Folder structure
driver-forecast-review/
├── scripts/
│   └── drivers.py
└── SKILL.md
Read SKILL.md

packs/forecast-planning/skills/driver-forecast-review/SKILL.md

---
name: driver-forecast-review
description: "Reviews a driver-based forecast change: recomputes revenue and EBITDA for the prior and current versions, walks the change driver by driver, and writes the forecast change note. Use when asked what changed in the forecast, forecast version comparison, driver walk, why EBITDA moved between versions, or a reforecast review. Needs the drivers CSV."
---

# Driver forecast review

Produces two files: the script's figures (`output/drivers.json`) and the
forecast change note (`output/drivers-note.md`). Unless the user names other
paths, write both to an `output/` folder in the current working directory.

The six drivers, the model and the walk order are in
`_shared/driver-model/SKILL.md`. Read it before writing the note.

## Steps

1. Find the drivers CSV the user named. Open only its header row. It needs
   `version`, `period`, `driver` and `value`. If any is missing, ask.
2. Run the script, `skills/driver-forecast-review/scripts/drivers.py`. As in
   every file in this pack, paths are relative to the pack root, the top
   folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/driver-forecast-review/scripts/drivers.py \
           --input <the CSV> --output output/drivers.json

   It compares the versions named Prior and Current. If the file names its
   versions otherwise, add `--prior <name> --current <name>`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the walk does not reconcile; never report its figures.
4. Read `output/drivers.json`. Use only the strings under `display`.
5. Write `output/drivers-note.md` to the contract below.
6. Check the note before you finish. Every figure in it must be a string
   from `display`, copied exactly with its parentheses. Delete any other
   figure.

## What the script proves

The script computes revenue, gross profit, opex and EBITDA for both versions
with the driver model, then walks Prior to Current one driver at a time in
the model's order. It exits 0 only when the six deltas sum to the EBITDA
change to the cent, the two revenue deltas sum to the revenue change, and
every driver that did not change has a delta of zero. `identity` in the JSON
records both sides.

## Output contract

The note has exactly these five sections, in this order:

    ## Headline
    One sentence, in this shape:
    Current EBITDA is <display.versions.current.ebitda> against
    <display.versions.prior.ebitda> in Prior, a change of
    <display.change.ebitda>, or <display.change.ebitda_pct>.

    ## Walk
    A table with columns Step and Amount and eight rows in this order:
    Prior EBITDA with display.versions.prior.ebitda; one row per entry in
    display.walk, in order, with its name and its delta, zero deltas
    included; Current EBITDA with display.versions.current.ebitda.

    ## Changed drivers
    A table with columns Driver, Prior, Current and Months changed, one row
    per entry in display.changed_drivers, in order: name, prior, current,
    months_changed.

    ## Review
    Two to four bullets. Each bullet starts with the name of a changed
    driver and asks its owner which documented assumption supports the
    change. In one of those bullets, say which delta carries the EBITDA
    move. Every bullet names a changed driver.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`

- Copy every figure exactly as it appears in `display`, parentheses included:
  write "a change of ($149K)", never "down $149K". Write counts in digits.
- Display figures are rounded. Never add, subtract or average them. Never
  state a difference between two figures (customers lost, points of margin,
  FTEs added) or a driver's share of the change; give both figures instead.
- Name drivers as `display.walk` names them, and versions as `labels` does.
- Give no reason for a change that the data does not show. Ask for it.
13-week cash forecastReview weekly cash balances, the cash trough and minimum-cash covenant headroom.
Inputs
Weekly cash CSV: week, start date, line, category and amount.
Outputs
  • cash13.json
  • cash13-note.md
Folder structure
cash-forecast-13wk/
├── scripts/
│   └── cash13.py
└── SKILL.md
Read SKILL.md

packs/forecast-planning/skills/cash-forecast-13wk/SKILL.md

---
name: cash-forecast-13wk
description: "Builds the 13-week direct cash forecast: weekly receipts, disbursements, closing balance and headroom against the minimum-cash covenant, and writes the cash memo. Use when asked for the 13-week cash forecast, weekly cash flow, cash runway, covenant headroom, liquidity forecast, or when cash dips below the minimum. Needs the weekly cash CSV."
---

# 13-week cash forecast

Produces two files: the script's figures (`output/cash13.json`) and the cash
memo (`output/cash13-note.md`). Unless the user names other paths, write
both to an `output/` folder in the current working directory.

## Steps

1. Find the weekly cash CSV the user named. Open only its header row. It
   needs `week`, `week_start`, `line`, `category` and `amount`. If any is
   missing, ask.
2. Run the script, `skills/cash-forecast-13wk/scripts/cash13.py`. As in every
   file in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/cash-forecast-13wk/scripts/cash13.py \
           --input <the CSV> --output output/cash13.json

   The minimum-cash covenant defaults to 750,000. If the user states another
   minimum, add `--minimum <amount>`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the roll-forward does not reconcile; never report its figures.
4. Read `output/cash13.json`. Use only the strings under `display`.
5. Write `output/cash13-note.md` to the contract below.
6. Check the memo before you finish. Every figure in it must be a string
   from `display`, copied exactly with its parentheses. Delete any other
   figure.

## What the script proves

Each week closes at the prior week's closing balance (the opening balance in
week 1) plus the week's receipts and disbursements. The script exits 0 only
when the opening balance plus every line equals the last week's closing
balance to the cent, and the line totals equal the sum of the weekly nets.
A week breaches the covenant when it closes below the minimum. The trough is
the lowest closing balance. The recovery week is the first week after the
last breach that closes at or above the minimum.

## Output contract

The memo has exactly these five sections, in this order:

    ## Headline
    One sentence, in this shape:
    Cash closes week <display.labels.horizon_weeks> at
    <display.totals.closing> against an opening balance of
    <display.totals.opening>, and the trough in week <display.trough.week>
    closes at <display.trough.closing> against the
    <display.covenant.minimum> minimum.

    ## Weekly forecast
    A table with columns Week, Week start, Receipts, Disbursements, Net,
    Closing and Headroom, one row per entry in display.weeks, in order.

    ## Covenant
    Two to four bullets: the breach weeks (display.breaches.weeks), the
    trough week with its closing balance and headroom (display.trough), the
    recovery week (display.breaches.recovery_week), and the headroom in the
    last week. Write a week as "week 5". Name no other week here.

    ## Actions
    Two to four bullets, with no amounts: levers that could lift the
    trough, such as collections before the trough week or vendor payments
    moved after it. Give each as an option for the treasurer, with the data
    needed to size it and the week by which it must be decided.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`

- Copy every figure exactly as it appears in `display`, parentheses included:
  write "a net of ($168K)", never "down $168K". Write counts in digits.
  Write the minimum as `display.covenant.minimum` prints it, not as the flag
  value.
- Display figures are rounded. Never add or subtract them. The Actions
  section holds no figure except week numbers.
- Refer to time as "week 5" or by a week_start date as `display` prints it.
- Never size a lever, move a payment or compute a new balance. Say what data
  would size it.
Scenario memoCompare base, upside and downside cases using the same planning model.
Inputs
Scenario CSV: scenario, driver, value and stated basis.
Outputs
  • scenarios.json
  • scenario-memo.md
Folder structure
scenario-memo/
├── scripts/
│   └── scenarios.py
└── SKILL.md
Read SKILL.md

packs/forecast-planning/skills/scenario-memo/SKILL.md

---
name: scenario-memo
description: "Computes base, upside and downside scenarios from annual drivers, walks each scenario's EBITDA change from base driver by driver, and writes the scenario memo. Use when asked for a scenario analysis, upside and downside cases, sensitivity of EBITDA to drivers, a scenario memo for planning, or a range for the forecast. Needs the scenarios CSV."
---

# Scenario memo

Produces two files: the script's figures (`output/scenarios.json`) and the
scenario memo (`output/scenario-memo.md`). Unless the user names other
paths, write both to an `output/` folder in the current working directory.

The six drivers, the model and the walk order are in
`_shared/driver-model/SKILL.md`. Read it before writing the memo.

## Steps

1. Find the scenarios CSV the user named. Open only its header row. It needs
   `scenario`, `driver`, `value` and `basis`. If any is missing, ask.
2. Run the script, `skills/scenario-memo/scripts/scenarios.py`. As in every
   file in this pack, paths are relative to the pack root, the top folder of
   the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/scenario-memo/scripts/scenarios.py \
           --input <the CSV> --output output/scenarios.json

   It walks every scenario from the one named Base. If the file names its
   base otherwise, add `--base <name>`.
3. If the script exits with an error, show the error and stop. Exit code 3
   means a walk does not reconcile; never report its figures.
4. Read `output/scenarios.json`. Use only the strings under `display`.
5. Write `output/scenario-memo.md` to the contract below.
6. Check the memo before you finish. Every figure in it must be a string
   from `display`, copied exactly with its parentheses. Delete any other
   figure.

## What the script proves

The script applies the driver model to each scenario's annual drivers, then
walks each scenario from Base one driver at a time in the model's order. It
exits 0 only when, for every scenario, Base EBITDA plus the six deltas equals
the scenario's EBITDA to the cent and every driver the scenario leaves at
its Base value has a delta of zero. The range is the lowest and the highest
EBITDA among the scenarios.

## Output contract

The memo has exactly these five sections, in this order:

    ## Headline
    One sentence, in this shape, where <base ebitda> is the ebitda of the
    base entry in display.scenarios:
    Base EBITDA is <base ebitda>, with a range from
    <display.range.low.ebitda> in <display.range.low.scenario> to
    <display.range.high.ebitda> in <display.range.high.scenario>.

    ## Scenarios
    A table with columns Scenario, Revenue, EBITDA, vs Base and %, one row
    per entry in display.scenarios, in order: scenario, revenue, ebitda,
    vs_base.change, vs_base.change_pct. The base row's last two cells are
    n/a, as display prints them.

    ## Drivers
    One bullet per changed driver in each scenario other than the base, in
    display order. A driver changed when its value differs from its
    base_value. Each bullet names the scenario and the driver, gives
    base_value to value, quotes the basis text exactly, and gives the delta.

    ## Recommendation
    Two to four bullets: which case to plan to, the signals that would move
    the plan to another case, and the decisions to prepare now.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`

- Copy every figure exactly as it appears in `display`, parentheses included:
  write "a change of ($309K)", never "down $309K". Write counts in digits.
- Display figures are rounded. Never add, subtract or average them. Never
  state a difference between two figures, a driver's share of a change, or a
  blend of two scenarios.
- Assign no probabilities. A recommendation names a case, it does not
  weigh cases.
Assumption registerFind planning assumptions that need an owner, a source or a fresh review.
Inputs
Assumption CSV with values, prior values, owners, sources, review dates, version and linked driver.
Outputs
  • register.json
  • register-note.md
Folder structure
assumption-register/
├── scripts/
│   └── register.py
└── SKILL.md
Read SKILL.md

packs/forecast-planning/skills/assumption-register/SKILL.md

---
name: assumption-register
description: "Reviews the planning assumption register: stale reviews, missing owners, unsourced changes and changed values, and writes the register review. Use when asked to review the assumptions, assumption register, which assumptions are stale or unowned, what changed in the planning assumptions, or an assumptions audit. Needs the assumptions CSV."
---

# Assumption register review

Produces two files: the script's figures (`output/register.json`) and the
register review (`output/register-note.md`). Unless the user names other
paths, write both to an `output/` folder in the current working directory.

The drivers that assumptions feed are in `_shared/driver-model/SKILL.md`.

## Steps

1. Find the assumptions CSV the user named. Open only its header row. It
   needs `assumption_id`, `area`, `assumption`, `value`, `unit`, `owner`,
   `source`, `last_reviewed`, `prior_value`, `changed_in_version` and
   `linked_driver`. If any is missing, ask.
2. Run the script, `skills/assumption-register/scripts/register.py`. As in
   every file in this pack, paths are relative to the pack root, the top
   folder of the pack, which holds its main SKILL.md:

       python3 <pack root>/skills/assumption-register/scripts/register.py \
           --input <the CSV> --output output/register.json

   Defaults: reviews are dated against 2026-01-01 (`--as-of`), a review
   older than 180 days is stale (`--stale-days`), and the version whose
   changes need a source is Current (`--current-version`). Pass a flag only
   when the user states another value.
3. If the script exits with an error, show the error and stop. Exit code 3
   means the counts do not reconcile; never report its figures.
4. Read `output/register.json`. Use only the strings under `display`.
5. Write `output/register-note.md` to the contract below.
6. Check the review before you finish. Every figure in it must be a string
   from `display`, copied exactly with its parentheses. Delete any other
   figure.

## What the script proves

The script tags each assumption: "stale review" when its last review is more
than the stale-days threshold before the as-of date, "no owner" when the
owner is blank, "unsourced change" when it changed in the current version
with no source. An assumption with a tag is flagged. A changed value is
listed under changes; it is not a finding by itself. The script exits 0 only
when flagged plus clean equals the number of assumptions, overall and summed
by area.

## Output contract

The review has exactly these five sections, in this order:

    ## Headline
    One sentence, in this shape:
    Of <display.counts.assumptions> assumptions, <display.counts.flagged>
    are flagged and <display.counts.by_type.stale> have a stale review.

    ## Findings
    A table with columns ID, Assumption, Value, Owner, Last reviewed and
    Finding, one row per entry in display.findings, in order:
    assumption_id, assumption, value, owner, last_reviewed, finding.

    ## Changes
    One bullet per entry in display.changes, at most six: the
    assumption_id and the assumption, prior_value to value, and the source,
    or that no source is recorded.

    ## Actions
    Two to four bullets: who reviews what before the plan locks. Where a
    flagged or changed assumption feeds a driver (linked_driver), say so in
    words. For an assumption with no owner, ask who should own it.

    ## Data notes
    One line in this form, with the backticks, where <file>, <rows> and
    <sha12> are the strings in display.input:
    <file>, <rows> rows, sha256 `<sha12>`

- Copy every figure, date and name exactly as it appears in `display`. Write
  counts in digits.
- Never compute a number of days, set an owner, or call a value right or
  wrong.
Pack routing instructions

packs/forecast-planning/SKILL.md

---
name: forecast-planning
description: "Produces forecast change notes, 13-week cash forecasts, scenario memos and assumption register reviews, with every figure computed by a script. Use when asked what changed between forecast versions or why EBITDA moved, for a forecast change note or driver walk, a 13-week or weekly cash forecast, a cash memo or covenant headroom, a scenario memo with base, upside and downside cases, or a review of the assumption register, including when given a drivers, scenarios, weekly cash or assumptions CSV. Use it instead of computing these figures by hand. Not for actuals commentary, KPI scorecards, close reviews or management reporting."
license: MIT
compatibility: Requires Python 3.9 or later (standard library only) and permission to run scripts. If scripts cannot run, the pack stops and says so; it never computes figures itself.
metadata:
  pack-version: "1.1.0"
  pack-standard: "skills-pack/1.0"
  house: "interstationary/1.0.0"
---

# Forecast and planning pack

This pack holds several skills. Pick the row that matches the request, read
every file listed for it, then follow the skill. Read the files before
answering; do not answer from memory.

All paths in this pack, in every file, are relative to this file's folder
(the pack root).

| Request | Read first | Then read |
| --- | --- | --- |
| What changed in the forecast, forecast version comparison, driver walk, why EBITDA moved | `skills/driver-forecast-review/SKILL.md` | `_shared/driver-model/SKILL.md`, `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| 13-week cash forecast, weekly cash, covenant headroom, liquidity, cash runway | `skills/cash-forecast-13wk/SKILL.md` | `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Scenario analysis, upside and downside, sensitivity, scenario memo, forecast range | `skills/scenario-memo/SKILL.md` | `_shared/driver-model/SKILL.md`, `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Assumption register, stale or unowned assumptions, what changed in the assumptions | `skills/assumption-register/SKILL.md` | `_shared/driver-model/SKILL.md`, `_shared/source-trace/SKILL.md`, `_shared/executive-voice/SKILL.md` |
| Anything else (actuals, variances, close, KPIs) | Not in this pack | Say so and stop |

## Rules for every skill in this pack

1. Numbers come from scripts. Never calculate, round or restate a figure yourself.
2. If a script fails or cannot run, report the error message and stop.
3. If the request is missing an input the skill needs, ask for it. Do not guess.

Getting started

Download and unzip the companion, then open README.md. It covers setup, dependencies and how to use the files alongside Chapter 1.

packs/
The four finance packs and their shared rules.
data/
Synthetic finance data and the capstone brief.
worksheets/
Templates for planning and building your own pack.
examples/
Chapter examples and saved outputs.
answers/
Exercise answers and the capstone reference pack.

Licenses

Book
Copyright © 2026 Felix Reyes. All rights reserved.
Code, skills, templates and sample data
MIT License

Read the full license scope.