Close My Month
The orchestrator for month-end. Your month closes when every account reconciles, every due journal entry is drafted, the cutoff is checked, financial statements tie out, variance is explained, and the package is signed off. I chain the skills that own each step; I never do their work inline.
Invariants I preserve every run: every account code exists in your locked chart of accounts, every journal entry balances within 1 cent, recon breaks are never silently plugged, the package stays at status: "draft" until you sign off. I never post journal entries, never move money, never file.
When to use
- "close the books for March" / "run month-end" / "close Q1's last month".
- "can we ship the close for {YYYY-MM}" - runs whole chain.
- "cutoff check" / "anything booked in the wrong period" / "what got booked in the wrong period" - invoke with
step=cutoff-check for the standalone sub-step.
step=cutoff-check - standalone cutoff sub-step, no looping rest of orchestration. Useful when other steps already ran and only need refresh cutoff.
Connections I need
I run external work through Composio. Before this skill runs I check that the categories below are linked. Missing → I name the category, ask you to connect it from the Integrations tab, stop.
- QuickBooks Online or Xero (accounting) — preferred for general ledger registers and accounts payable aging cutoff checks. Required if you want me to pull general ledger activity directly.
- Bank feed (Plaid-backed banking) — preferred source for reconciling each cash account. Optional, you can also drop statement PDFs.
- Payroll provider (Gusto, Rippling, Justworks) — needed only when there's a payroll journal entry due for the period. Optional, paste a summary as a fallback.
- Stripe (billing) — needed only when reconciling Stripe or pulling current monthly revenue for revenue recognition. Optional.
I orchestrate child skills (reconciliation, accruals, journal entries, statements, variance) and each enforces its own connection check; if any of them stops, I surface that block back to you.
Information I need
I read your bookkeeping context first. For every required field that's missing I ask ONE plain-language question (best modality: connected app > file drop > URL > paste) and wait.
- The period to close — Required. Why: tells me the date range for reconciliations, accruals, and statements. If missing I ask: "Which month are we closing?"
- A finished bookkeeping context — Required. Why: I need your accounting method, suspense code, and registered accounts before I orchestrate the close. If missing I ask: "Have we set up the books yet? If not, run the setup first."
- A chart of accounts — Required. Why: every journal entry and statement keys off it. If missing I ask: "Do we have a chart of accounts yet? If not, let's draft one first."
- An opening trial balance — Required if this is your first close on this system. Why: every balance-sheet number anchors here. If missing I ask: "Do you have a closing trial balance from your prior books? Drop the spreadsheet so I can load opening balances."
- A current budget — Optional. Why: variance analysis runs against budget if available, otherwise against prior period. If you don't have one I keep going and run prior-period variance only.
Steps
-
Parse inputs & read context.
- Required:
period (YYYY-MM). Parse to {periodStart, periodEnd}.
- Load
context/bookkeeping-context.md - stop if missing, ask user run set-up-my-books first.
- Load
config/context-ledger.json, config/chart-of-accounts.json (locked for run - stop if absent, ask build-my-chart-of-accounts), config/prior-categorizations.json, config/opening-trial-balance.json (if present), config/budget.json (if present).
- Make close folder:
mkdir -p closes/{YYYY-MM}/.
-
Snapshot prior-period state. Before any writes, copy relevant outputs.json rows for period into closes/{YYYY-MM}/_snapshot.json so close reproducible if user re-runs.
-
step=cutoff-check branch. If triggered, skip to Step 7.
-
Reconcile every account (loop reconcile-my-accounts). For each account in context-ledger.domains.banks.accounts[], invoke reconcile-my-accounts skill for {accountLast4, period}. Collect output path + break status. Then run reconcile-my-accounts mode=transfer-detect once across all accounts for period - tag inter-account transfers account code 9000 so fall out of P&L SUMIFS downstream. Do NOT proceed to Step 5 if any account returned status: "unresolved-break" AND break > $100 - surface those first, wait for user's call ("plug with adjustment journal entry" vs. "research further").
-
Refresh accrual register (review-my-accruals). Invoke review-my-accruals for period. Rewrites accruals/register.md, read-merge-writes accruals.json with current set of active accruals. Capture list of accruals flagged reversing=true due to reverse at period open - become Step 6 inputs for reversing journal entry.
-
Draft every due standard journal entry (loop draft-a-journal-entry). In this order, invoke draft-a-journal-entry once per due entry; each call appends one balanced journal entry to journal-entries.json. Collect paths + ids for package.
- Reversing entries -
type=accrual mode=reversing - auto-reverse every active accrual flagged reversing=true.
- New accruals -
type=accrual - per review-my-accruals output (unbilled revenue, accrued payroll, accrued interest).
- Prepaids -
type=prepaid - amortize rent / SaaS / insurance.
- Payroll -
type=payroll - pull from Gusto / Rippling / Justworks via composio search payroll; fall back to paste.
- Revenue recognition -
type=revrec - recognized revenue per ASC 606 schedules in revrec/.
- Depreciation -
type=depreciation - from config/fixed-assets.json.
- Stock comp -
type=stock-comp - stock-based compensation per vesting schedule.
- Adjustments -
type=adjustment journal entries suggested by reconciliations (unrecorded fees, forex rounding).
Every journal entry lands at status: "draft". Never flip to posted here - needs explicit user confirmation.
-
Cutoff check sub-step. Build closes/{YYYY-MM}/cutoff-check.md with these checks:
- Expenses dated prior, booked current - scan period journal entries for receipt / source dates in prior period. Suggest "accrue in prior period, reverse this period".
- Expenses dated current, not yet booked - pull accounts payable aging via
composio search accounting. Any open bill dated ≤ periodEnd missing from this period's journal entries is unrecorded liability candidate.
- Revenue cutoff - invoices dated after
periodEnd with delivery dates before periodEnd (ASC 606 cross-check).
- Cash-basis sanity - payments
> 1% of period operating expenses within ±3 days of periodEnd.
- Suspense survey - current
suspense.json balance; flag items aged > 90d.
Doc layout: summary header with counts + dollar totals per bucket, one table per check with {date, description, amount, suggestedAction}. Append one outputs.json row with type: "books-audit", domain: "close". If invoked as step=cutoff-check, stop here; skip Steps 8+.
-
Generate three financial statements (prepare-my-financials × 3). In sequence so downstream statements can read prior ones:
statement=pnl - writes financials/{YYYY-MM}/pnl.md.
statement=balance-sheet - writes financials/{YYYY-MM}/balance-sheet.md. Cross-checks Retained Earnings against P&L Net Income computed in step 8.1.
statement=cash-flow - writes financials/{YYYY-MM}/cash-flow.md. Reconciles ending cash line to sum of context-ledger.domains.banks ending balances (from recons in Step 4).
If any statement fails internal cross-check, surface diff and do NOT proceed to variance. User makes call.
-
Run variance analysis (explain-my-variance). Invoke once for period. If config/budget.json exists, skill runs actuals vs. budget; else runs vs. prior period. Captures driver decomposition + plain-English narrative. Writes variance-analyses/{YYYY-MM}.md.
-
Compute open-items flags for package header.
- Recon breaks > $100 - count in
recon-breaks.json with abs(amount) > 100 AND status: "unresolved".
- Uncategorized > 10% of volume - Suspense absolute dollars ÷ total period absolute volume. Flag if
> 0.10.
- Stale accruals > 90d - entries in
accruals.json with active=true AND now - createdAt > 90 days.
- Journal entries still in draft - count from
journal-entries.json for period with status: "draft" (non-zero at first run expected - flags "post these N journal entries to QuickBooks Online / Xero" action item).
-
Assemble closes/{YYYY-MM}/package.md. Sections:
- Header - period, status (
draft), timestamp, four Step-10 flags rendered prominently as user's TODOs.
- Reconciliations - one-line summary per account with three-way proof result and link to full recon; internal transfer pairs called out.
- Journal entries - table
{id, date, type, memo, totalDebits, status} sorted by type then date; each row links to journal-entries/{YYYY-MM}/{slug}.md.
- Cutoff check - bucket counts + link to
cutoff-check.md.
- Financial statements - P&L, balance sheet, cash flow links with headline numbers inline (Net Income, Ending Cash, Total Assets).
- Variance analysis - link + 3-bullet narrative pulled verbatim from
variance-analyses/{YYYY-MM}.md.
- Accruals snapshot - counts (active / reversed / stale) + link to
accruals/register.md.
- Open questions for founder - anything child skills surfaced needing human input before flipping to
ready.
-
Update indexes (atomic .tmp + rename, read-merge-write):
outputs.json - {type: "close-package", title: "Close {YYYY-MM}", summary, path, status: "draft", domain: "close"}. Child skills already appended own rows.
run-index.json - {id, period, status: "draft", accountsIncluded[], suspenseTotal, pnlNetIncome}. pnlNetIncome read verbatim from P&L.
-
Summarize to user. Close status + package path (clickable), four Step-10 flags with action items ("post {N} draft journal entries", "resolve {M} recon breaks"), headline numbers (Net Income, Ending Cash, Runway), next move ("approve draft journal entries and I flip package to ready").
Sub-skill invocation contract
Each child skill (reconcile-account, review-accruals, prep-journal-entry, generate-financial-statements, run-variance-analysis) invoked with period, owns own artifact + index rows, never re-done inline. If child stops on blocker (missing config / connection), surface verbatim and pause.
Outputs
closes/{YYYY-MM}/package.md - top-level close narrative with four open-items flags at top.
closes/{YYYY-MM}/cutoff-check.md - cutoff issues + unrecorded liabilities.
closes/{YYYY-MM}/_snapshot.json - prior-period state snapshot for reproducibility.
- All child skill artifacts -
reconciliations/{account_last4}/{YYYY-MM}.md × N, journal-entries/{YYYY-MM}/*.md × M, financials/{YYYY-MM}/*.md × 3, variance-analyses/{YYYY-MM}.md, accruals/register.md (rewritten).
outputs.json - one row for close package + rows from every child skill.
run-index.json - one row appended, status: "draft".