Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions skills/bookkeeper/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
name: bookkeeper
description: Categorize a bounded transaction batch against an existing chart of accounts and return a sealed, read-only reconciliation without inventing accounts or mutating a ledger. Use when an operator needs explainable GL mapping, anomaly flags, and an explicit needs-review lane.
registry_owner: ArgonautWorks
---

# Bookkeeper

Use this skill to reconcile a supplied transaction batch against a supplied
chart of accounts. The result is evidence for review, not authority to post a
journal entry. This skill performs no ledger mutation, bank action, payment,
file write, network request, or downstream dispatch.

## Operating model

1. Validate every transaction, account, and prior-period boundary before
categorizing anything. Transaction identifiers and account codes must be
unique. Dates must be real ISO calendar dates and amounts must be finite,
non-zero numbers.
2. Treat `chart_of_accounts` as the entire account universe. Never create,
infer, rename, or substitute a GL account outside that input.
3. Honor a transaction's explicit `account_code` only when that exact code is
present in the chart. Otherwise compare normalized description tokens with
each account's name and bounded `keywords` list.
4. Categorize only when one account has a unique positive match. Record the
chart account code and name, confidence, reason, source transaction, and
matched terms.
5. Leave tied, unknown, or explicitly invalid account bindings unmatched.
Return `needs_review` with a concrete reason instead of guessing.
6. Flag duplicates and dates outside `prior_period.start_date` through
`prior_period.end_date`. These anomalies remain read-only observations.
7. Reconcile the batch by reporting matched and unmatched counts and amount
totals. Confirm the result's no-write proof before handing it to an
operator.

## Inputs

- `transactions[]`: objects with `id`, ISO `date`, `description`, finite
non-zero `amount`, and optional `currency` and `account_code`.
- `chart_of_accounts[]`: objects with unique `code`, `name`, optional `type`,
and optional `keywords[]`. All keywords are evidence supplied by the caller.
- `prior_period`: an object with inclusive ISO `start_date` and `end_date`,
plus optional prior reconciliation facts for operator context.

Do not paste secrets, bank credentials, card data, or unredacted personal data
into these inputs. Use stable transaction identifiers and already-admitted
accounting evidence.

## Outputs

- `categorized[]`: only matched lines. Every item binds to one exact input
account and includes `confidence` and `reason`.
- `anomalies[]`: duplicate, out-of-period, or unmatched observations tied to a
transaction identifier.
- `reconciliation`: `matched`, `unmatched`, counts, amount totals, and the
overall `reconciled` or `needs_review` status.
- `needs_review`: whether human review is required and why.
- `read_only`: an explicit proof surface with `ledger_mutation: false`, empty
`writes`, empty `external_effects`, and zero write count.

The receipt proves the bounded computation and output contract. It does not
prove a journal was posted because no posting occurs.

## Recovery rules

- Add an account to the chart only through the caller's normal chart-governance
process, then rerun with the updated chart. This skill never adds it.
- Resolve a tie by supplying an exact existing `account_code` on the source
transaction or by repairing the chart keywords. Do not lower the unique
match requirement.
- Correct invalid dates, amounts, duplicate identifiers, or period bounds at
the evidence source and rerun. Do not silently coerce them.
- A `needs_review` result is final for the supplied batch. No ledger write may
be inferred from it.

## Agent rules

- Never invent a GL account, transaction, amount, date, confidence, or reason.
- Never mutate a ledger or claim that a read-only result was booked.
- Never hide unmatched transactions to make reconciliation appear complete.
- Keep every categorized line traceable to its transaction and chart account.
- Return the sealed receipt with the result for independent verification.
190 changes: 190 additions & 0 deletions skills/bookkeeper/X.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
skill: bookkeeper
version: "0.1.0"

catalog:
kind: skill
audience: public
visibility: public
role: context
execution: execute
completion: runtime_receipt
requires_adapter: true
approval: none

policy:
side_effects: none
sends_messages: denied
mints_authority: denied
persists_state: denied
secrets_required: false

harness:
cases:
- name: sealed_clean_batch_reconciles_read_only
runner: reconcile
inputs:
transactions:
- { id: txn-001, date: "2026-07-03", description: AWS monthly cloud hosting, amount: -120, currency: USD }
- { id: txn-002, date: "2026-07-08", description: Stripe customer payout, amount: 850, currency: USD }
- { id: txn-003, date: "2026-07-12", description: Office Depot supplies, amount: -45, currency: USD }
chart_of_accounts:
- { code: "4000", name: Sales Revenue, type: revenue, keywords: [stripe, payout, sales] }
- { code: "6100", name: Cloud Hosting, type: expense, keywords: [aws, cloud, hosting] }
- { code: "6200", name: Office Supplies, type: expense, keywords: [office, depot, supplies] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31", opening_balance: 1000 }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 3
unmatched: 0
status: reconciled
needs_review:
required: false
reasons: []
read_only:
ledger_mutation: false
write_count: 0
writes: []
external_effects: []
receipt: { schema: runx.receipt.v1 }

- name: refused_ambiguous_transactions_return_needs_review
runner: reconcile
inputs:
transactions:
- { id: txn-ambiguous, date: "2026-07-15", description: General monthly service, amount: -75, currency: USD }
chart_of_accounts:
- { code: "6100", name: General Services, type: expense, keywords: [general, service] }
- { code: "6200", name: Service Expenses, type: expense, keywords: [general, service] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31", opening_balance: 0 }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 0
unmatched: 1
status: needs_review
needs_review:
required: true
reasons: [ambiguous_account_match]
read_only:
ledger_mutation: false
write_count: 0
writes: []
external_effects: []
receipt: { schema: runx.receipt.v1 }

- name: explicit_existing_account_binding_is_explainable
runner: reconcile
inputs:
transactions:
- { id: txn-explicit, date: "2026-07-18", description: Annual service renewal, amount: -300, currency: USD, account_code: "6300" }
chart_of_accounts:
- { code: "6300", name: Software Subscriptions, type: expense, keywords: [software, subscription] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31" }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 1
unmatched: 0
status: reconciled
read_only:
ledger_mutation: false
write_count: 0
receipt: { schema: runx.receipt.v1 }

- name: unknown_explicit_account_never_invents_gl_account
runner: reconcile
inputs:
transactions:
- { id: txn-unknown, date: "2026-07-20", description: New vendor charge, amount: -90, currency: USD, account_code: "9999" }
chart_of_accounts:
- { code: "6100", name: Cloud Hosting, type: expense, keywords: [cloud, hosting] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31" }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 0
unmatched: 1
status: needs_review
needs_review:
required: true
reasons: [unknown_explicit_account]
read_only:
ledger_mutation: false
write_count: 0
writes: []
receipt: { schema: runx.receipt.v1 }

- name: out_of_period_anomaly_requires_review_without_write
runner: reconcile
inputs:
transactions:
- { id: txn-late, date: "2026-08-01", description: AWS cloud hosting, amount: -60, currency: USD }
chart_of_accounts:
- { code: "6100", name: Cloud Hosting, type: expense, keywords: [aws, cloud, hosting] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31" }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 1
unmatched: 0
status: needs_review
needs_review:
required: true
reasons: [out_of_period]
read_only:
ledger_mutation: false
write_count: 0
writes: []
external_effects: []
receipt: { schema: runx.receipt.v1 }

- name: invalid_amount_refuses_before_reconciliation
runner: reconcile
inputs:
transactions:
- { id: txn-zero, date: "2026-07-20", description: Invalid zero line, amount: 0, currency: USD }
chart_of_accounts:
- { code: "6100", name: Miscellaneous Expense, type: expense, keywords: [invalid] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31" }
expect:
status: failure

runners:
reconcile:
default: true
type: cli-tool
command: /usr/bin/env
args: [node, run.mjs]
outputs:
categorized: array
anomalies: array
reconciliation: object
needs_review: object
read_only: object
artifacts:
named_emits:
reconciliation: reconciliation
inputs:
transactions:
type: array
required: true
description: Bounded transaction lines to categorize without mutation.
chart_of_accounts:
type: array
required: true
description: Complete allowed GL account universe for this run.
prior_period:
type: object
required: true
description: Inclusive reconciliation period and optional prior facts.
29 changes: 29 additions & 0 deletions skills/bookkeeper/fixtures/ambiguous-needs-review.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: bookkeeper-ambiguous-needs-review
kind: skill
target: ..
runner: reconcile
inputs:
transactions:
- { id: fixture-ambiguous, date: "2026-07-15", description: General service, amount: -80, currency: USD }
chart_of_accounts:
- { code: "6100", name: General Services, type: expense, keywords: [general, service] }
- { code: "6200", name: Service Expenses, type: expense, keywords: [general, service] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31" }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 0
unmatched: 1
status: needs_review
needs_review:
required: true
reasons: [ambiguous_account_match]
read_only:
ledger_mutation: false
write_count: 0
writes: []
external_effects: []
receipt: { schema: runx.receipt.v1 }
metadata: { public_skill: bookkeeper, source_case: ambiguous-needs-review-no-write }
27 changes: 27 additions & 0 deletions skills/bookkeeper/fixtures/clean-batch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: bookkeeper-clean-batch
kind: skill
target: ..
runner: reconcile
inputs:
transactions:
- { id: fixture-aws, date: "2026-07-05", description: AWS hosting, amount: -100, currency: USD }
- { id: fixture-stripe, date: "2026-07-06", description: Stripe sales payout, amount: 500, currency: USD }
chart_of_accounts:
- { code: "4000", name: Sales Revenue, type: revenue, keywords: [stripe, sales, payout] }
- { code: "6100", name: Cloud Hosting, type: expense, keywords: [aws, hosting] }
prior_period: { start_date: "2026-07-01", end_date: "2026-07-31" }
expect:
status: sealed
output:
subset:
reconciliation:
matched: 2
unmatched: 0
status: reconciled
read_only:
ledger_mutation: false
write_count: 0
writes: []
external_effects: []
receipt: { schema: runx.receipt.v1 }
metadata: { public_skill: bookkeeper, source_case: clean-read-only-reconciliation }
Loading