# Journal Entry Builder

> Use this skill whenever the user wants to construct, format, or validate a journal entry against their Mosofin workspace. Triggers include: 'book a JE for...', 'create a journal entry to record...', 'how do I record this transaction', 'make me an accrual JE', 'reclass these entries', 'reverse this entry', or any request involving debits and credits to specific accounts. Also trigger for adjusting entries, correcting entries, closing entries, intercompany entries, and import-ready JE files for accounting systems. Workspace-scoped: it confirms the workspace, discovers which company files are connected and which read-only tools are enabled, then builds entries against the real chart of accounts and validates them against the books — checking that accounts exist, signs match account types, the period is open, and no prior accrual already covers the item. Mosofin never posts: every entry is a proposal for a person to post. Do NOT use for GL coding decisions only — use gl-coding-assistant. Do NOT use for full perio

- Skill: `mosofin/journal-entry-builder` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mosofin/journal-entry-builder`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mosofin/journal-entry-builder/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Finance & Business
- Author: MosoFin (https://skillmd.com/u/mosofin)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mosofin/journal-entry-builder

---


<!-- shared:onboarding-inline start -->
## Before you start — this skill works with or without Mosofin

**With Mosofin connected**, the skill reads your live accounting data through the
gateway: the figures come from your own books, it validates against the real chart of
accounts, and most steps run automatically.

**Without it, the skill still works.** No subscription, no connector, or a skill copied
on its own — you are not blocked and you are not asked to buy anything first. The
Mosofin gates are skipped and you are asked for what each step needs instead: a trial
balance, a statement, an export, the documents themselves. **The accounting logic, the
edge cases and the output standards are identical** — only where the numbers come from
changes, and the output always says which is which.

**You choose, and you are asked.** Where a connection exists, the skill asks at the
start whether to use it for this run or whether you would rather supply the data
yourself — **a connected gateway is not taken as consent to read your books.** Say no
and it runs manually without asking again.

### Strict rule — this skill never changes your data

**This skill will never write, update or delete existing data in any data source.**
Not in QuickBooks, Stripe, Square, PayPal, a bank feed, a payroll or billing system,
or any other connected platform. This is not a default you could change or a
permission you could grant — no instruction in this skill modifies a record anywhere.

It will **never**:

- create, edit, overwrite, void or delete a record in a connected platform
- invoke a write operation, or ask you to approve or enable one — a write tool is out
  of scope even when your policy has it enabled
- direct you to update, overwrite or delete existing data in a data source
- copy or move data from one connected platform into another

What it does instead is **read, and propose.** Every entry, schedule, reconciliation
and document it produces is a **draft for you to review.** Where it finds a problem —
a duplicate, a mismatch, a stale balance — it describes the problem and proposes a
correcting entry as a draft. It does not tell you to delete or overwrite the original,
and it never acts on one itself.

Whether anything reaches your books is a decision you make outside this skill, in your
own system, by your own hand. **If you act on none of it, nothing in your data has
changed.**

### Onboarding — required whenever Mosofin is connected

**If the Mosofin gateway is connected, onboarding is not optional and not per-skill.**
Before any skill reads anything, the workspace and the data sources in it must be
confirmed with you. It is the same sequence for every Mosofin skill, so it is kept in
one place rather than repeated in each:

- in this repo: [`shared/onboarding.md`](../../shared/onboarding.md)
- installed on its own, or you would rather read the product docs:
  [docs.mosofin.com/start-here/quickstart](https://docs.mosofin.com/start-here/quickstart)

**Already onboarded this workspace?** Then you have answered it once and will not be
asked again from scratch — but **the confirmation itself still happens every run.**
Gate 0 reads your workspace back and waits for an explicit yes; Gate 1 settles which
company file. Those are not skippable, and no data is read before them.

**No Mosofin connector? The skill still works.** If the gateway is not present at all,
there is nothing to onboard: the gates are skipped, every step becomes `[manual]`, and
you are asked for what each step needs — a trial balance, a statement, an export.
**The accounting work is unchanged**; only the data source is. You will be told this
once, and you will not be asked to install anything before being helped.

What follows in **Part A** is not more onboarding. It is this skill exploring what your
confirmed workspace and data sources can actually do — which tools exist, which serve
this particular request — so the run is shaped around your books rather than a generic
template.

---
<!-- shared:onboarding-inline end -->
# Journal Entry Builder (Mosofin)

Constructs **properly balanced journal entries** for any accounting scenario — **standard, adjusting,
accrual, deferral, reclass, reversing, intercompany, or closing**. Outputs entries that **balance to the
penny**, with complete narration and **import-ready format** for the user's accounting system.

**In plain words:** every accounting event gets recorded as at least two lines that cancel out — something
gained against something given up. Getting the two sides right, against the right accounts, on the right
date, with an explanation someone can follow a year later, is the whole job.

This skill is **system-agnostic**. **It does not invent account numbers or assume any jurisdiction's tax
rules.**

It is **workspace-scoped**: the chart of accounts, the account types, the existing balances and the
validation evidence come from tool calls against a company file connected to your Mosofin workspace in this
conversation, or from something you supplied by hand and that is labelled as such.

**Note it is no longer chart-of-accounts-agnostic in the original's sense** — it reads the entity's actual
chart rather than accepting any list. That is a narrowing, stated rather than implied.

## Mosofin never posts — and this is the skill where that matters most

**Dozens of skills in this pack end by handing off here.** Depreciation, accruals, impairments, releases
from restriction, elimination entries, shrinkage, deferred tax — **all of them arrive at this skill to
become an entry.**

> **Mosofin is read-only. It cannot post a journal entry, and it will not.** **Everything this skill
> produces is a proposal** — a fully constructed, validated, import-ready entry **for a person to review and
> post.**
>
> **That is not a limitation to apologise for; it is the correct division.** Posting is an authorised act
> with a preparer and a reviewer behind it, and an entry arriving pre-validated with its support attached is
> what makes that review fast. **Say "proposed entry" on every output**, and never imply anything has been
> recorded.

## What the workspace changes: validation stops being aspirational

The original's Step 6 asks you to *verify* nine things. **In a Mosofin workspace, six of them become
queries** rather than intentions:

| Validation | Original | Here |
|---|---|---|
| **Debits = Credits** | arithmetic | arithmetic — **[auto]** |
| **All accounts exist in the COA** | verify against a supplied list | **[auto]** against the live chart |
| **Date is within an open accounting period** | "warn if not" | **[gated]** — often determinable |
| **Sign conventions match account types** | judgment | **[auto]** — **account types are readable** |
| **Memo explains the business purpose** | judgment | judgment |
| **References to source documents** | judgment | **[gated]** — the document may be in the system |
| **Reversing flag set correctly** | judgment | judgment |
| **No P&L accounts in a pure BS reclass** | verify | **[auto]** — account types again |
| **Tax lines present where warranted** | jurisdiction | **[gated]** — codes readable, rules not |

**And it adds the check the Pitfalls section names first, which is the most valuable one here:**

> **Pitfall 1 — "Recording a payment when an accrual already exists → double-counts."** **[auto]**: **the
> accrual balance is readable.** Before proposing a payment or invoice entry, **check whether an accrual for
> the same item is sitting open.** This is the classic double-count, it is invisible at the moment of
> posting, and it is one query.

Three more from the same section:

- **Pitfall 2 — a receipt where an invoice exists** should clear AR, not go to revenue. **[auto]**: the open
  invoice is findable.
- **Pitfall 5 — a loan payment booked entirely as expense.** **[auto]**: the loan liability balance and its
  movement are readable, which shows the principal portion.
- **Manual JEs to revenue, cash, AR or AP control accounts** — the original flags these as auditor red
  flags. **[auto]**: whether the target is a control account is readable from the chart, so the flag raises
  itself.

**What stays outside:** the transaction or event being recorded — that is the user's — the materiality
threshold, the tax rules, and any source document not held in the system.

---

# ONBOARDING — Confirm the workspace and its data sources

**Required for every skill, every run — whenever Mosofin is connected.** Gates 0 and 1
settle *which books this is about*: the workspace, and the data sources inside it.
**Part A then explores what those confirmed sources can actually do** and personalises
the run around them. Nothing is read before Gate 0 is answered.

**If the Mosofin tools are not present at all, skip this part.** There is nothing to
onboard: say so once, then run the skill manually on data the user supplies. See the
precondition check below.

Run Gates 0 → 1 → 2 → 3 in this order, before building anything. This ordering is the contract. Do not
skip a gate because a previous conversation covered it — connections, permissions, and company files
change between periods.

Call the Mosofin tools by the **bare names your own tool list exposes** — `list_workspaces`,
`get_agent_datasources`, `get_datasource_tools`, `invoke_datasource_api_tool`, `get_skills`,
`get_my_skill`, `create_skill`. Do not add a `mosofin_` prefix and do not hardcode a client-side
`mcp__…` namespace; that string is composed by whichever MCP client is running.

<!-- shared:scope-protocol start -->
### First — ask whether to use Mosofin for this run

Two things decide how this skill runs, and they are settled **before Gate 0**.

**1. Are the Mosofin tools present at all?** — `list_workspaces` and the rest of the
gateway. Check before doing anything else.

**2. If they are present, ask the user. Once, in these terms:**

> Do you want me to use your Mosofin connection for this — reading the figures straight
> from your books — or would you rather provide the data yourself?

**Wait for the answer.** A connected gateway is **not** consent to read from it, and
this skill does not open with a data read. Never assume, never auto-pick.

- **Use Mosofin** → onboarding is required. Run Gates 0-1 to confirm the workspace and
  its data sources, then Part A explores what those sources expose.
- **Provide the data myself** → **skip Gates 0-2 entirely** and run manually, exactly as
  though no connector were present. **Do not ask again during the run.** Raise it once
  more only if the user asks for something their supplied data cannot answer, and then
  as an offer, not a demand.

**If the tools are not present, do not ask** — there is nothing to choose. The skill was
copied on its own, the connector was never added, or there is no subscription.
**Do not make connecting a condition of helping.** Say once, plainly, that Mosofin is
not connected and this run will be manual, then **carry on with the skill's normal
workflow**: ask for what each step needs — a trial balance, a statement, an export, the
documents themselves — and do the accounting work on what the user provides.

**In manual mode**, whether chosen or unavoidable:

- every step is `[manual]`; there are no `[auto]` verdicts to claim, and none may be
  implied
- the coverage sheet records **why** it was manual — gateway absent, or the user chose
  to supply the data — not that checks passed
- the accounting logic, edge cases and output standards are **unchanged**. That is the
  part of this skill that never depended on a connection
- mention **once** that connecting Mosofin would automate the manual steps, with a link
  to [docs.mosofin.com](https://docs.mosofin.com). Do not raise it again, and never
  withhold work to press the point

#### What a manual run actually does, gate by gate

| Gate | In a manual run |
|---|---|
| **Gate 0** — workspace | **Skipped.** There is no workspace to confirm. |
| **Gate 1** — data sources | **Skipped as a discovery step.** Still ask *which entity or company this work is for*, by name, so every output can be labelled — but record it as **user-asserted**, not confirmed against a connection. |
| **Gate 2** — capability map | **Skipped.** The map is not empty, it is uniform: **every task is `[manual]`.** |
| **Gate 3** — profile, then interview | **Runs, and grows.** The profile half cannot run — there is no company-profile tool — so everything it would have derived silently becomes a **question**: base currency, fiscal calendar, country or region, time zone. Then the interview runs in full, and **every row of the Inputs table that would have been `[auto]` becomes something to ask for.** |

Then **Part B runs unchanged** on what the user supplied.

**Ask the user to upload the data, and name the formats.** A manual run does not mean
retyping anything. Say plainly what to upload, in what form, and what each item is for —
then read it from the files they provide.

| Ask for | Upload as |
|---|---|
| Ledger detail, trial balance, transaction listings | **CSV** or **XLSX** export, or a pasted table |
| Statements and third-party documents | **PDF** or **CSV**, or a clear photo / scan |
| Invoices, bills, receipts, remittances | **PDF** or **image** — a single file or a batch |
| Short facts — a date, a balance, a policy | typed straight into the chat |

**Ask for the whole set up front, as a checklist, not drip-fed.** A person collecting
exports would rather be given one list than be interrupted six times. Mark which items
are strictly **required** and which merely improve the result, so they can decide how
much to gather.

**Confirm what actually arrived before starting the work.** Name each file, say what was
read from it — period covered, row count, opening and closing balances — and list what
is still outstanding. If a file is unreadable, covers the wrong period, or does not
contain what its name suggests, **say so at once**. Never work around a bad input
silently, and never guess at a column you cannot identify — ask.

**If something cannot be supplied, say what the output will and will not be — before
doing the work.** Never estimate a figure that was meant to come from the books, never
fill a gap with a plausible number, and never present a partial result as complete. An
honest partial answer, clearly labelled, is the correct outcome.

**Everything the user provides is evidence like any other.** Reconcile it, check it,
and challenge it where it does not tie. Manual input is not more trustworthy than a
ledger read — it is less, because nothing validated it on the way in.

**Present but not authenticated is not the same as absent.** If the tools are there and
a call returns a `reconnect_url` or an auth error, surface it and let the user choose —
reconnect, or continue manually. Do not silently fall back.

### Confirming scope — workspace, then data sources, then tools

**Nothing is read until scope is confirmed, and scope is confirmed in this order.**
Each step depends on the answer to the one before it, so none of them may be skipped,
merged, or guessed at.

| # | Question to the user | How it is settled |
|---|---|---|
| 1 | **Which workspace?** | `list_workspaces` with **no arguments**. Read the workspace back **by name** and wait for an explicit yes. On `selection_required`, ask whether this is single- or multi-workspace, then which **by name**, then call again with `workspace_ids=[…]` and `mode="single"`/`"multi"`. |
| 2 | **Which data sources, in that workspace?** | `get_agent_datasources` with the confirmed `workspace_id`. `connected: true` is in scope; `connected: false` is **excluded and named as excluded**, with any `reconnect_url` surfaced. Then settle the entity scenario — single-entity: which company; multi-entity: which set — always by `display_name`. |
| 3 | **Which tools do those sources actually expose?** | `get_datasource_tools` per in-scope datasource, **and per company file when several are live — permissions are per company.** This is discovery, not a question: read what is there before promising anything. |

**Never auto-pick.** Not the workspace, not the company file, not the entity scenario.
**Silence is not a yes**, and an answer to one question is not an answer to the next.

**Names, never internal ids.** Name the workspace and refer to companies by
`display_name`. Never print an internal numeric tenant id, and never show a raw
`data_source_id` — pass the opaque handle, show the name.

**Only then does the work begin.** Once the workspace, the data sources and their tools
are confirmed, resolve every task against what was actually found: what is available
now decides which steps are `[auto]`, which are `[gated]` and which fall to `[manual]`.
Where the confirmed tools cannot answer the request, say so and ask — do not substitute
an assumption for a capability.

**The catalogue is authoritative.** Take exact `tool_name` values from the Gate 2
listing — names are not uniformly styled, some underscored, some hyphenated.
**Do not invent a tool name.** On `UNKNOWN_TOOL`, read the valid names from the error
and retry.
**Never call a tool whose `effective_policy` is `disabled`.**

**This map is built fresh every run** and held only for this run. It is written out in
the coverage sheet, never written back into this file.
<!-- shared:scope-protocol end -->

## Gate 0 — Confirm the workspace

Call `list_workspaces` with **no arguments**.

- **One workspace** → read the workspace **name** back and **wait for an explicit yes**.
- **Two or more** (`selection_required`) → ask **in chat** whether this is single- or
  multi-workspace, then which workspace(s) **by name**, then call again with `workspace_ids=[…]`
  and `mode="single"` / `mode="multi"`.

Never auto-pick. Never print an internal numeric tenant id — name the workspace, pass the opaque
`ws_…` handle.

**When this skill is invoked as a handoff from another skill**, the gates have usually run already in the
same conversation. **Confirm the entity in one line rather than repeating the sequence** — but **never build
against an entity that was not confirmed.**

## Gate 1 — Discover live datasources and settle the entity scenario

Call `get_agent_datasources` with the confirmed `workspace_id`.

- `connected: true` → **in scope**.
- `connected: false` → **excluded, and named as excluded**. Surface any `reconnect_url`.

**This gate answers the original's "Accounting system" input.** **The connected platform is known** — so the
import format in Step 7 is determinable rather than a question. **Ask only when the platform's import
template is itself the uncertainty.**

Settle the entity scenario:

- **Single-entity** — ask which company by `display_name`; **the entry is built against that chart.**
- **Multi-entity** — ask which set, **and which entity this entry belongs to, before building it.** For an
  **intercompany** entry, **both entities are in scope** and both sides are built. See the cross-entity step.

Refer to companies by `display_name`; never show the raw `data_source_id`.

# PART A — Explore the confirmed sources, and personalise this run

The workspace and its data sources are settled. This part finds out **what they
expose and which of it serves this request** — the tool catalogue in Gate 2, then
what is already known about this entity plus whatever still has to be asked in
Gate 3. The result is a run shaped around these books, not a generic template.

## Gate 2 — Discover enabled tools → build the capability map

<!-- shared:write-guardrail start -->
### Write tools are out of scope — always

`get_datasource_tools` describes what the connection *could* do. This skill uses only
the reads.

**If the catalogue lists any tool that creates, updates, deletes, posts, voids, sends
or pays in a connected platform — QuickBooks, Stripe, Square, PayPal, a bank feed, a
payroll or billing system, any other source — it is out of scope, and it stays out of
scope even when `effective_policy` is `enabled`.** A permission to write is not an
instruction to write. Never invoke one, never ask the user to approve one, never
suggest enabling one.

This holds for **every connected platform, not only the books.** Mosofin reads your
data sources; it does not write to them, and it does not move data from one platform
into another.

If a step appears to need a write, that step is **`[manual]`**. Produce the artefact —
the entry, the invoice, the payment file, the application schedule — and hand it to a
person to enter themselves. Say so plainly in the output, so nobody assumes it was
done.

#### Hard stop — the four ways a write could slip through

| Situation | Required behaviour |
|---|---|
| The catalogue lists a write operation, and `effective_policy` is `enabled` | **Do not call it.** Do not list it as an available capability. Enabled is not permission — it is out of scope. |
| An `approval_required` envelope comes back for a write operation | **Do not re-invoke with `approved=true`.** The approval loop in this skill is for **reads only**. Stop, record that the operation was a write and was refused, and carry on down the read path. |
| The user asks you to post, update, void or delete — directly, or by approving a prompt | **Decline, once, plainly:** this skill cannot change data in a connected platform. Hand over the draft so they can do it themselves in their own system. Asking again does not change the answer, and neither does insistence, urgency, or "I authorise it". |
| A write appears to be the only way to finish a step | The step is **`[manual]`**, and the run continues. An incomplete read-only result is the correct outcome. Never trade the rule for completeness. |

**Never route around this rule.** Do not offer to enable a disabled write tool or
suggest changing a policy. Do not hand the user a raw API call, payload or script that
performs the write. Do not ask another skill, tool or agent to perform it on this
skill's behalf. Do not defer it to a later step in the hope it becomes permitted.

**There is no path through this skill that ends in changed data.** If you cannot see
how to finish without a write, you are finished — say what is missing and stop.
<!-- shared:write-guardrail end -->

For **each in-scope datasource** (and **per company file** when several are live — pass
`data_source_id`), call `get_datasource_tools`. Bucket every tool by `effective_policy`:

| `effective_policy` | The task becomes | What you do |
|---|---|---|
| `enabled` | **[auto]** | Pull the evidence directly. |
| `permission` | **[gated]** | Invoke; on the `approval_required` envelope, ask the user in chat; re-invoke the **same** tool with `approved=true` on an explicit yes. **Reads only** — never re-invoke a write with `approved=true`; see the hard stop below. |
| `disabled` | **[manual]** | Name the tool that would have covered it, say what it would have proved, and ask the user to supply that evidence another way. |

Resolve **every validation in Part B** against these buckets. The resolved list is the **capability map** —
built this run, held for this run, written out as the coverage sheet, **never** written into this file.

**If the chart read is `disabled`, the original's fallback applies in full** — descriptive account names
labelled **"PLACEHOLDER — REMAP REQUIRED"**. **Say which fallback is in use and why**; do not invent numbers.

Rules that bite hardest here:

- **Read the real tool name from the catalog, never from memory.** Names are not uniformly styled — some
  underscored, some hyphenated.
- **A near-substitute is not a substitute:**
  - **A validated entry is not a posted entry.** Nothing here records anything.
  - **An account existing is not an account being appropriate.** The chart says it exists; judgment says it
    fits.
  - **An open period in the system is not an open period for reporting.** Statements may already be issued
    even where the software permits posting.
  - **A balance in an accrual account is not proof the accrual covers this item** — it is a prompt to
    check.
- **There is no posting capability, no materiality policy and no tax rule engine on this surface.**

## Gate 3 — Profile the entity, then interview the user

Call the platform's company-profile tool (on QuickBooks, `get_company_info`) for each in-scope entity.

**Derive silently** what the profile answers: legal name, **functional currency**, fiscal calendar and
year-end, country / region.

**Ask the user** what actually changes the work — the original Inputs table, **minus the four the connected
books now answer**:

| What to confirm | Required? | Notes |
|---|---|---|
| **Transaction or event to record** | **Required — [manual]** | The user's. |
| **Posting date** | **Required** | **[gated]** to validate against period status. |
| ~~Chart of accounts~~ | **Now [auto]** | Real accounts, real types. **No placeholders unless the read fails.** |
| ~~Entity / company~~ | **Now [auto]** | Confirmed by `display_name`. |
| ~~Functional currency~~ | **Now [auto]** | From the profile. |
| **Reference / source document** | Recommended — **[gated]** | Findable where the document is in the system. |
| ~~Accounting system~~ | **Now [auto]** | From Gate 1. **Ask only for the import template if needed.** |
| **Reversal flag** | Optional | |
| **Tax jurisdiction** | Required if tax effects involved — **[gated]** | **Country `[auto]`; the rules `[manual]`.** |
| **Materiality threshold** | Recommended — **[manual]** | Needed for the large-entry flag; **do not assume one.** |
| **Confirm scope** | **Required** | Read back the entity by `display_name` and the period. |
| **Confirm manual evidence** | **Required** | The event, the tax rules and the threshold are `[manual]`. |

**If no chart of accounts is available**: **ask the user for the specific account names and numbers they
use. Do not invent.** Acceptable fallback: **descriptive account names with a clear "PLACEHOLDER — REMAP
REQUIRED" label.**

Ask as **one short batch**. Propose defaults where reasonable — but **never** default an account, a tax
treatment, or a materiality threshold.

**On later runs**, read stored preferences first (Step 9), confirm in one line, and ask only what changed.
The account map, the entry templates, the import format and the numbering convention persist; **balances and
validation results are re-obtained every time.**

---

# PART B — The domain work

Every step below is the original procedure, unchanged in count, order, or substance, with plain-language
wording, an `[auto]` / `[gated]` / `[manual]` verdict, and the typical evidence tool added.

**Never drop a task because no tool covers it.** The event itself and the tax rules are legitimately
`[manual]`.

Tool names in *italics* are typical. Resolve real names and policies from your Gate 2 catalog.

## Step 0 — Fetch the evidence (grounding) — Mosofin addition

**Batch independent reads into one message** — the chart, the balances and the validation populations do
not depend on each other. Never serialize them.

The server is **stateless**: pass `data_source_id` on **every** call, including retries.

Typical batch, per in-scope entity:

- *`search_accounts`* — **the chart with account numbers, names and types** — usually **[auto]**. **The
  single most important read here**
- *`get_trial_balance`* / *`get_balance_sheet`* — **current balances on the accounts being touched** —
  usually **[auto]**
- *`get_general_ledger`* on **the accrual, prepaid, deferred and clearing accounts** — **the Pitfall 1
  check** — usually **[auto]**
- *`search_invoices`* / *`search_bills`* — **whether a document already exists for this item** — usually
  **[auto]**
- *`search_journal_entries`* — **the numbering convention in use, and whether the period still accepts
  entries** — usually **[auto]**
- *`get_company_info`* — legal name, **functional currency**, year-end — usually **[auto]**

Handle the envelopes:

- `approval_required` → ask the user in chat, then re-invoke the same tool with `approved=true`.
- `entity_required` → ask by `display_name`, then pass that `data_source_id`.
- `tool_policy_disabled` → convert that validation to **[manual]** and record the gap. **If it is the chart
  read, state that placeholder coding is in use.**
- `UNKNOWN_TOOL` → read the valid names from the error; do not guess.
- Dead connection → surface the `reconnect_url`.

Check the **`mock` flag**. `mock: true` is fixture data — **an entry validated against fixture accounts may
reference accounts that do not exist in the real chart**, and it would be imported.

## Step 1 — Identify the JE type

| Type | When to use | Typically reverses? |
|------|-------------|---------------------|
| **Standard** | Recording an actual transaction — cash receipt, payroll, sale | No |
| **Accrual** | **Expense incurred but not yet invoiced or paid, or revenue earned but not invoiced** | **Usually yes, first day of next period** |
| **Deferral** | **Cash received or paid in advance of revenue or expense recognition** | **No** — released over time |
| **Adjusting** | **Period-end true-up** — depreciation, amortization, FX reval, accruals | Sometimes |
| **Correcting** | **Fixing a prior error** | **No** — document the original |
| **Reclass** | **Moving balance from one account to another, no economic event** | No |
| **Reversing** | **Reverses a prior accrual on the first day of the next period** | **Is itself the reversal** |
| **Closing** | **Year-end close of P&L to Retained Earnings** | No |
| **Intercompany** | **Cross-entity transactions; mirror entries in both entities** | **Mirrored** |
| **Opening Balance** | **New entity setup / system migration** | No |

**State the JE type explicitly in the header.**

*Mosofin note on Closing*: **check whether the platform closes the year automatically** before proposing a
closing entry — see `closing-entries-and-trial-balance`. **[auto]**: P&L accounts at zero after year-end
with no visible closing journal means the system did it, and **a manual closing entry would double-close
retained earnings.**

## Step 2 — Apply the accounting equation

**Every JE must balance: Total Debits = Total Credits.**

**Normal balance rules:**

| Account type | Increases with | Decreases with |
|--------------|----------------|----------------|
| **Asset** | Debit | Credit |
| **Liability** | Credit | Debit |
| **Equity** | Credit | Debit |
| **Revenue** | Credit | Debit |
| **Expense** | Debit | Credit |
| **Contra-asset** (e.g. Accumulated Depreciation) | **Credit** | **Debit** |
| **Contra-revenue** (e.g. Sales Returns) | **Debit** | **Credit** |

**Walk through substance:**

1. **What did the entity receive?** → **Debit** (asset / expense) or Credit reduction
2. **What did the entity give up?** → **Credit** (asset reduced) or Debit reduction
3. **What obligation was created or settled?** → **Credit** (created) or **Debit** (settled)

**[auto]** enforcement: **account types are readable**, so **the sign convention is checkable rather than
assumed.** A debit to a revenue account, or a credit to an expense account, is not wrong in itself — but
**it is unusual enough to warrant the memo saying why**, and the check surfaces it.

## Step 3 — Construct the entry

Standard format:

```
Date: YYYY-MM-DD
Entry #: [auto or user-supplied]
Type: [Standard / Accrual / Reclass / etc.]
Reference: [Invoice #, contract, supporting doc]
Memo: [One line describing the business event]

Account No. | Account Name | Debit | Credit | Line Memo
------------|--------------|-------|--------|----------
[from user's COA] | [from user's COA] | $X | | [purpose]
[from user's COA] | [from user's COA] | | $X | [purpose]

Total                                 | $X | $X | ✓ Balanced
```

**Required for every entry:**

- **Posting date**
- **Entry type**
- **Header memo** — business purpose in plain language
- **≥ 2 lines** — one DR, one CR
- **Line-level memos when multiple debits or credits**
- **Balance check at bottom**

**Mosofin additions to the header, since the entry travels beyond this conversation:**

- **PROPOSED ENTRY — NOT POSTED**, stated plainly
- **The entity by `display_name`**
- **The validation result** from Step 6
- **The source**: which skill or analysis produced it, and the tool calls behind any figure

**Account numbers and names come from the live chart** (*`search_accounts`*, **[auto]**) — **exactly as they
appear there**, including formatting, so the import matches.

## Step 4 — Common JE patterns

`DR` is a debit, `CR` a credit. **Every pattern below is a proposal.**

### Accrual (expense incurred, not yet invoiced)
```
DR  Expense account                       $X
    CR  Accrued Liabilities (BS)              $X
Memo: Accrue [service] for [period] — invoice expected [date]
```
**Flag as reversing on first day of next period.**

### Prepaid expense (paid in advance)
```
At payment:
DR  Prepaid Expense (BS)                  $X
    CR  Cash / AP                             $X

Periodic amortization:
DR  Expense                               $X / periods
    CR  Prepaid Expense                       $X / periods
```
**Suggest `prepaid-amortization-schedule` for the full schedule.**

### Deferred revenue
```
At receipt:
DR  Cash                                  $X
    CR  Deferred Revenue (BS)                 $X

As earned:
DR  Deferred Revenue                      $X / period
    CR  Revenue                               $X / period
```

### Depreciation
```
DR  Depreciation Expense                  $X
    CR  Accumulated Depreciation              $X
Memo: Periodic depreciation — [asset class or specific asset]
```

### Reclass between accounts
```
DR  Correct Account                       $X
    CR  Incorrect Account                     $X
Memo: Reclass [item] from [wrong] to [correct]; original entry #, date
```
**[auto]**: **the original entry is findable** (*`search_journal_entries`*, *`get_general_ledger`*), so the
memo's entry number and date can be filled from the ledger rather than from memory.

### Correcting prior error

**Never delete or alter the original entry if posted.** Either:

- **Reverse and rebook** — full reversal + correct entry — **preferred for material errors or audit-trail
  needs**
- **Net correction** — just the difference — **for small true-ups**

**Reference the original entry # in the memo either way.**

*Mosofin note*: **Mosofin cannot delete or alter anything**, which enforces the first sentence structurally.
And **the original entry is readable**, so the reversal can be built from what was actually posted rather
than from what someone remembers posting.

### Intercompany

**Always create mirror entries in both entities:**
```
Entity A (lender):
DR  Intercompany Receivable — Entity B    $X
    CR  Cash                                  $X

Entity B (borrower):
DR  Cash                                  $X
    CR  Intercompany Payable — Entity A       $X
```
**Flag for `intercompany-reconciliation` at period end.**

**[gated]**, and stronger here than the original assumes: **where both entities are connected, both sides
are built against their own real charts** — the account names differ between entities and **using the
correct ones in each is the difference between an entry that imports and one that fails.** **Where the
counterparty is not connected, build the near side and say the far side could not be built against a real
chart.**

### Reversing entries

**Date: first day of next period. Flip debits and credits from the original. Reference the original entry
#.** **[auto]**: the original is readable, so the flip is mechanical and the reference is exact.

### Closing entries (year-end)

**Close revenues and expenses to Income Summary — or directly to Retained Earnings if the system supports
it. Verify net result ties to P&L net income / loss.** **[auto]** to verify: *`get_profit_and_loss`* gives
the figure the entry must produce.

## Step 5 — Tax effects (jurisdiction-driven)

**Only include tax lines if the user provided a jurisdiction and the transaction has tax implications.**

**For sales / output side:**
- **VAT / GST / sales tax collected → Liability** (Tax Payable / Output Tax)
- **Multi-jurisdiction sales → separate lines per jurisdiction**
- **Tax-exempt or zero-rated → no tax line; note the exemption reason**

**For purchases / input side:**
- **Recoverable input tax** (where the jurisdiction allows it) **→ Asset** (Input Tax Receivable)
- **Non-recoverable input tax → included in expense**

**If the jurisdiction's rules aren't clear, ask the user. Do not assume.**

**[gated]**: **the tax accounts and any configured tax codes are `[auto]`**, so a tax line can name a real
account and a real code — **but which code applies remains `[manual]`.** See `gl-coding-assistant`, where
the same split appears.

## Step 6 — Validation

Before delivering, verify — **and here most of these are performed, not merely intended**:

- ✅ **Debits = Credits, to the penny** — **[auto]**
- ✅ **All accounts exist in the provided COA** — **[auto]** against the live chart. **An account that does
  not exist is a hard stop**, not a warning
- ✅ **Date is within an open accounting period (warn if not)** — **[gated]**: **entries existing after a
  date, or the platform's own period status, indicate it.** **Warn about reporting status too** — a period
  the software will accept may already have been reported
- ✅ **Memo explains the business purpose** — not "JE" or "adjustment"
- ✅ **References to source documents included** — **[gated]**; findable where the document is in the system
- ✅ **Reversing flag set correctly if applicable**
- ✅ **Sign conventions match account types** — **[auto]** from account types
- ✅ **No P&L accounts used for pure BS reclasses without a stated reason** — **[auto]** from account types
- ✅ **Tax lines present if jurisdiction + transaction warrant** — **[gated]**

**Mosofin additions to the validation set — the Pitfall checks, run as queries:**

- ✅ **No existing accrual already covers this item** — **[auto]**. **The Pitfall 1 double-count check.**
  Read the relevant accrual account before proposing a payment or invoice entry
- ✅ **No open invoice exists for a receipt being booked to revenue** — **[auto]**. Pitfall 2
- ✅ **A loan payment is split between interest and principal** — **[auto]** support from the liability
  balance. Pitfall 5
- ✅ **The target is not a control account** — **[auto]**: **manual entries to revenue, cash, AR or AP
  control accounts are auditor red flags**, and whether an account is one is readable
- ✅ **The entry does not exceed materiality without a review flag** — **[gated]**; **the threshold is
  `[manual]`**
- ✅ **A clearing or suspense account used here is expected to clear** — **[auto]** to report its current
  balance and age

**Report the validation result with the entry.** A proposal that says "validated: 9 of 9 checks passed;
account 6410 confirmed in chart; period open; no matching accrual found" is one a reviewer can post
quickly. **One that says "validated" is not.**

## Step 7 — Output format

**Single JE → markdown table inline. Multiple JEs / import-ready file → `.xlsx` or `.csv`.**

**Generic columns:**

| Date | Entry # | Account No. | Account Name | Debit | Credit | Memo | Reference | Class/Dept | Currency |

**System-specific column headers** — **and the connected platform is known from Gate 1**, so **use its
format by default** rather than asking:

- **QuickBooks Online**: `JournalNo`, `JournalDate`, `Line Description`, `Account`, `Debit`, `Credit`
- **Xero**: `*Narration`, `*Date`, `Description`, `*AccountCode`, `*TaxRate`, `*Amount`
- **NetSuite**: `External ID`, `Date`, `Account`, `Debit`, `Credit`, `Memo`, `Subsidiary`, `Department`,
  `Class`
- **Sage Intacct / 50 / 200**: **format varies — ask the user for the import template**
- **Microsoft Dynamics 365 / Business Central**: `Journal Batch`, `Posting Date`, `Document No.`,
  `Account Type`, `Account No.`, 

…(truncated)
