Finance · Month-End Close

Month-End Close: Re-Architecture Plan

Same correct numbers every run, on either machine, with humans only on the genuine judgment calls.

For: Anthony + Will Status: Plan, pre-build Target: ~90% automated / ~10% human reasoning
01

The Goal

A month-end close that produces the same correct numbers every time it runs, on either machine, with humans only doing the small set of genuine judgment calls. Target is roughly 90% automated and 10% human reasoning, which is fine and expected.

02

The Core Idea

One sentence: Separate the working data (per-skill, append one row per month) from the main P&L (written by a single assembler), and gate every number with a fail-closed check against an independent source.
02b

Open Questions to Confirm with Will

COGS source. Will mentioned a spreadsheet that pulls in / updates COGS. Confirm: is that the COGS source of truth, or is sku cost v2.xlsx (what the skills join to today)? And can COGS come straight from the API connection Will already uses (Sellerboard's per-SKU product cost, or whatever system already holds it) instead of a hand-maintained file? Per "one canonical source, no drift," if an authoritative system already holds COGS, pull from it directly — a parallel xlsx is exactly the file that drifts and caused the COGS-inflation bug. (Anthony: confirm whether you meant Salesforce or Sellerboard — the skills today recon against Sellerboard, which carries per-SKU cost.)
03

Architecture: Three Layers

Layer 1 · Inputs

Raw files

Settlement .txt, orders exports, Paycor xlsx, SKU cost, Sellerboard CSV. These must live in ONE shared, agreed location with a fixed naming convention, so Anthony's machine and Will's machine feed identical files. Determinism is meaningless if the inputs differ. Each skill prints the input filenames and row counts it used.

Layer 2 · Working Sheet (NEW)

The staging layer

One new Google Sheet, separate from the main P&L.

  • One tab per skill (or a few skills grouped by shared source data, e.g. one Amazon Settlement tab, one Payroll tab, one P&L Channel Inputs tab).
  • Each month appends one new row per tab. Flat table: columns are the fields that skill produces.
  • No blocks, no inserts, no stacking, no per-month column discovery. Appending a row is the lowest-risk write there is.
  • This is where Will and the auto-QC review numbers before anything reaches the main P&L.
Layer 3 · Main P&L

The existing master doc

Written by ONE assembler skill only. It reads the validated month row from each staging tab and writes the corresponding P&L columns. Because it is the only writer to the master, we harden one write path instead of thirteen.

04

Why This Kills the Current Bug Classes

Wrong column / wrong row / insert-shift math
Gone. Staging is append-a-row; the assembler writes fixed master cells.
One skill overwriting another's data
Structurally impossible. Each skill owns its own tab.
Cross-month wipes (the April-wipe)
Gone. Months are separate rows, never overwritten.
Silent wrong numbers (the summary.py bug)
Caught by the recon gate below.
05

Non-Negotiable: The Fail-Closed Recon Gate

"Deterministic" is not enough. A wrong script is consistently wrong. Every staging row, when written, is checked against an independent source and aborts if it diverges beyond tolerance:

Checks are code, not a Claude eyeball. A QC dashboard can display green/red, but the checks themselves are deterministic assertions. Claude re-reading its own work and saying "looks right" is exactly what failed in the May close.
06

Where Reasoning Is Allowed — the ~10%

Reasoning is permitted ONLY for genuinely ambiguous calls, and only as a flag for human approval, never an auto-write:

Guardrail for each: Claude proposes and explains, Will approves, the approved value is then written deterministically. Plus a sanity-range check on any reasoned value. We list every reasoning step explicitly during the audit so the human-judgment set is a known checklist, not a surprise.
07

The Work, in Phases

Phase 1

Audit + determinize every skill (a back-and-forth between both Claudes)

This is a loop, not a one-way handoff. Anthony's Claude reviews a skill and sends feedback. Will's Claude reviews it, applies what fits, and pushes back with the context Anthony's side doesn't have (Will's machine holds the real data, file layouts, and history). It repeats until the skill is agreed-solid. Anthony's side is the second opinion to make the skill efficient and correct; Will's side is the source of truth on how it actually runs. Acceptance test per skill: it does its own write, its own read-back assertion, and its own recon gate; Claude does no arithmetic and chooses no cells. For any reasoning step, both sides agree why and define its guardrail first.

Phase 2

Build the working spreadsheet

Create the new staging sheet; decide the tab grouping; define each tab's column schema (the fields per skill). Migrate each skill to append its row there.

Phase 3

Build the assembler (Will's skill, on Will's machine)

One skill, living and running on Will's computer, that reads the month's validated staging rows and writes the main P&L. It carries the full pre-flight: honor the user-provided gid/column verbatim, print every write target, insert-not-overwrite, run the recon, abort on FAIL.

Phase 4

Auto-QC in a SEPARATE session (before any human QC)

A dedicated QC skill runs in its own fresh session, with no memory of how the numbers were produced, and checks them cold against source data and the recon gates. It runs FIRST and outputs a green/red report. Only after it passes does Will do his manual QC. The separate session matters: a fresh agent can't rationalize a number it didn't compute, so it catches what the producing session would wave through. That is the exact failure from the May close, where the producing session declared its own output correct.

Phase 5

Dependency map (run order) + orchestrator

Write the dependency map — the run order, "what must run before what" (e.g. amz-settlement to cm/wip fills; brand P&L to channel P&L to software-splits to recurring; payroll to channel payroll rows). Build ONE orchestrator that runs the skills in that encoded order in a single session. Order is encoded, never guessed live. Whole-pipeline dry-run (no writes) first, so Will sees all numbers and recon results before the assembler touches the main P&L.

Phase 6

Run manually, prove it

Run the orchestrator on demand. Acceptance test: Anthony runs the full pipeline on his machine, Will runs it on his, same inputs, same day, numbers match to the cent. This proves true determinism. Do NOT schedule until this passes.

Phase 7

Schedule (local cron/launchd, not a remote routine)

Once proven, use a local launchd/cron job on the Mac to fire the orchestrator on the close date. It must be local, not a remote /schedule agent, because the pipeline reads local input files and uses local MCP connections that a remote agent wouldn't see. The Mac just needs to be awake at run time.

08

Division of Labor

Anthony

Runs the second-opinion side in his Claude — reviews each skill, proposes the recon gates and reasoning guardrails, sends feedback to Will.

Will

Owns the skills and the data. His Claude applies fixes and pushes back with real context; he owns the assembler (on his machine), the staging-tab QC, and the two-machine reproducibility test.

Both

Agree the tab grouping, the dependency map, and the human-judgment checklist before locking. Phase 1 runs as a back-and-forth between the two Claudes.

Order to execute: Phase 1 (audit/determinize) and Phase 2 (staging sheet) first — they remove most of the pain. Phases 3-5 wire it together. Don't touch Phase 7 (scheduling) until Phase 6 (two-machine match) passes.