jaz-clio 5.63.0 → 5.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-api
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill whenever you call, debug, or review code that touches the Jaz
6
6
  REST API. Covers field names, response shapes, 159 production gotchas, error
@@ -149,22 +149,28 @@ Turn APPROVED claims into journal entries, and record books-only employee payout
149
149
  The expense-claim members. CLI: `clio employees …`. `list = search_employees` (no
150
150
  bare list endpoint).
151
151
 
152
- - **`add_employee`** — `name`, **`userResourceId`, and `claimProfileResourceId` are
153
- required** (the server rejects a create without a user to bind:
154
- `422 EMPLOYEE_USER_RESOURCE_ID_MISSING`). **`userResourceId` is the user's resourceId —
152
+ - **`add_employee`** — **`name` and `claimProfileResourceId` are required** (without a
153
+ profile: `422 EMPLOYEE_CLAIM_PROFILE_REQUIRED`). `userResourceId` is optional: omit it
154
+ for an offline employee with no login. **`userResourceId` is the user's resourceId —
155
155
  read it as the `userResourceId` FIELD on an org-user (`search_org_users`), NOT the
156
156
  org-user record's own `resourceId`; passing the membership id returns
157
- `422 EMPLOYEE_USER_NOT_FOUND`.** Each user binds to at most one employee. Binding is
158
- **PERMANENT** (rotation blocked once set). An **offline employee** (no user bound yet) can
159
- still arise via import bind it later with `bind_employee_user`. Dedups by email (per-org unique).
157
+ `422 EMPLOYEE_USER_NOT_FOUND`.** Each user links to at most one employee per org. The link
158
+ can be changed later: `update_employee` `userResourceId` relinks, `clearFields: ["userResourceId"]`
159
+ unlinks. An **offline employee** (no user bound yet) can also arise via import; bind it with
160
+ `bind_employee_user` or `update_employee` `userResourceId`. Dedups by email (per-org unique).
160
161
  - **The approver comes from the claim profile, not the employee.** An employee's approver
161
162
  is `claimProfile.approverUserResourceId` (set on the Claim Profile) — the employee record
162
163
  has no own approver field. To change who approves, edit the profile or move the employee
163
164
  to another profile. `claimProfileResourceId` is locked while the employee has unsettled claims.
164
- - **`update_employee`** — partial (omit = no change; email `""` clears). **Archive with
165
- `active: false`** (reversible prefer over `delete_employee`). `userResourceId` is NOT
166
- editable here; for an **offline employee** (no user bound yet), use **`bind_employee_user`**
167
- (one-way, permanent only while unbound). `clearEmploymentType` unsets the classification.
165
+ - **`update_employee`** — partial: an omitted, `null` or `""` param is left unchanged (the
166
+ tool drops them, because the raw API treats an explicit `null` on email, phone,
167
+ managerEmployeeResourceId, employmentType or userResourceId, and `phone: ""`, as a clear).
168
+ To clear, list the fields in **`clearFields`** (`email`, `phone`, `managerEmployeeResourceId`,
169
+ `employmentType`, `userResourceId`); a field both set and cleared in one call is refused.
170
+ One exception: `email: ""` is passed through and clears email, same as `clearFields: ["email"]`.
171
+ `userResourceId` links or relinks the login user; `clearFields: ["userResourceId"]`
172
+ unlinks it. **Archive with `active: false`** (reversible, prefer over `delete_employee`).
173
+ `bind_employee_user` only binds an employee that has no user yet.
168
174
  - **`delete_employee`** — server validates the employee is settled (else error). Prefer archive.
169
175
  - **`search_employees`** / **`search_employee_balances`** — the second is the balance
170
176
  directory (per-currency reimbursement owed). `search_employee_payouts` lives in `claim_processing`.
@@ -176,7 +182,8 @@ bare list endpoint).
176
182
  - **Import**: `preprocess_employees_file` (sync — pass a sheet `fileUrl`, returns a row
177
183
  preview) → `import_employees` (`create`/`update`/`delete` arrays, **max 100 each**;
178
184
  sync-validates rows with row-level 422s, then queues an async job — poll
179
- `search_background_jobs`). Create rows need a bound user + claim profile.
185
+ `search_background_jobs`). Create rows need `name` + `claimProfileResourceId`;
186
+ `userResourceId` is optional (omit for an offline employee).
180
187
 
181
188
  ---
182
189
 
@@ -930,8 +930,8 @@ Two of the seven never reach you through this path: a zero `adjustmentValue` and
930
930
  **Fix**: Find an expense GL account via `search_accounts` — `accountType` is a **display label** (`"Operating Expense"`, `"Direct Costs"`), NOT an enum like `"EXPENSE"` (which returns zero rows) — then pass its `resourceId` as `expenseAccountResourceId`.
931
931
 
932
932
  ### "EMPLOYEE_USER_NOT_FOUND" (422)
933
- **Cause**: `add_employee` / `bind_employee_user` was given a `userResourceId` that isn't a user ("User not found for userResourceId …"). The usual mistake: passing an org-user record's own `resourceId` instead of its `userResourceId`.
934
- **Fix**: Use `search_org_users` and read the member's **`userResourceId`** field (not the org-user record's `resourceId`), then retry. Each user binds to at most one employee, and the binding is permanent once set.
933
+ **Cause**: `add_employee` / `update_employee` / `bind_employee_user` was given a `userResourceId` that isn't a user ("User not found for userResourceId …"). The usual mistake: passing an org-user record's own `resourceId` instead of its `userResourceId`.
934
+ **Fix**: Use `search_org_users` and read the member's **`userResourceId`** field (not the org-user record's `resourceId`), then retry. Each user links to at most one employee per org. To move a link, relink with `update_employee` `userResourceId` or unlink with `clearFields: ["userResourceId"]`.
935
935
 
936
936
  ### "EMPLOYEE_CLAIM_PROFILE_REQUIRED" (422)
937
937
  **Cause**: `add_employee` was called without `claimProfileResourceId` ("A claim profile is required for an employee — it carries the employee balance account used to convert and pay claims"). The claim profile is **server-required** — the org default is NOT auto-applied for employees (unlike claims), even when a default profile exists.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-cli
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill when running Clio CLI commands, building shell scripts with
6
6
  Clio, debugging auth issues, understanding --json output, paginating results,
@@ -268,9 +268,9 @@ Same subcommands and flags as `cash-in`.
268
268
  | `search` | `--name`, `--email`, `--active`, `--claim-profile-resource-id`, `--sort`, `--order` |
269
269
  | `balances` (alias `search-balances`) | per-employee, per-currency reimbursement owed |
270
270
  | `get <id>` | `--json` |
271
- | `create` | `--name`, `--user`* (bind login), `--claim-profile`*, `--employment-type`, `--email`, `--manager` |
272
- | `update <id>` | `--name`, `--claim-profile`, `--employment-type`, `--archive` / `--activate`, `--clear-employment-type` |
273
- | `bind-user <id> <userId>` | bind a login user to an offline employee (no user yet) one-way, only while unbound |
271
+ | `create` | `--name`*, `--claim-profile`*, `--user` (link login, optional), `--employment-type`, `--email`, `--manager` |
272
+ | `update <id>` | `--name`, `--email`, `--phone`, `--manager`, `--claim-profile`, `--employment-type`, `--user` (link/relink), `--archive` / `--activate`, `--clear-email`, `--clear-phone`, `--clear-manager`, `--clear-employment-type`, `--unlink-user` |
273
+ | `bind-user <id> <userId>` | bind a login user to an offline employee (no user yet); relink or unlink via `update` |
274
274
  | `delete <id>` | only if settled (no outstanding balance) |
275
275
  | `preprocess <fileUrl>` | `--file-type` (CSV / XLS / XLSX) — preview rows before import |
276
276
  | `import` | `--create` / `--update` / `--delete` (JSON arrays) — async, returns jobId |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill when migrating accounting data into Jaz — importing from Xero,
6
6
  QuickBooks, Sage, MYOB, or Excel exports. Covers the full conversion pipeline:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-kit
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill when an accountant, bookkeeper, or owner is running real books
6
6
  in Jaz across one or more organizations from the terminal — setting up a
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-pseudo-sql
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill when answering ad-hoc data questions that aren't covered by
6
6
  download_export (canonical reports — anomaly, audit, aging, P&L, BS, GL,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-jobs
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill for recurring accounting workflows — month/quarter/year-end
6
6
  close, bank reconciliation, GST/VAT filing, payment runs, credit control,
@@ -110,7 +110,7 @@ Cross-check: `generate_trial_balance(period_end: '2025-01-31')`. Sum credit move
110
110
 
111
111
  For each existing `Prepaid Expenses` capsule (via `search_capsules(filter: {status: {eq: 'ACTIVE'}})` (capsule type is not filterable — see `building-blocks.md` § Filter limits)):
112
112
 
113
- 1. **STOP — do not select these with a filter.** Journals cannot be narrowed to one capsule: `JournalFilter` declares no `capsuleResourceId`, a journal row carries no capsule link even at `view: 'full'` or on `GET /journals/{id}`, and `GET /capsules/{id}` returns only `totalTransactions` a count (all measured 2026-09-07). A date+status search returns EVERY matching DRAFT in the org, including drafts a practitioner deliberately parked, so passing it to `bulk_update_journals(saveAsDraft: false)` finalizes unrelated work. Surface the capsule and its expected journal count and let the practitioner identify the journals to finalize.
113
+ 1. **STOP — do not select these with a filter.** Journals cannot be narrowed to one capsule by a filter: `JournalFilter` declares no `capsuleResourceId`, and `GET /capsules/{id}` returns only `totalTransactions`, a count (measured 2026-09-07). A date+status search returns EVERY matching DRAFT in the org, including drafts a practitioner deliberately parked, so passing it to `bulk_update_journals(saveAsDraft: false)` finalizes unrelated work. `get_journal` (`GET /journals/{id}`) does return the link as `capsule: {resourceId, type, title}` (measured 2026-09-23), so check each candidate journal's `capsule.resourceId` against this capsule, keep only the matches, and confirm the count with the practitioner before finalizing.
114
114
  2. If empty: either the recipe was set up wrong (no journal for this period — investigate via `search_journals` without status filter to see if it's already ACTIVE, then skip), OR the practitioner went off-recipe. Surface to practitioner.
115
115
  3. If found: collect resourceIds, then `bulk_update_journals(items: [{resourceId: <id>, saveAsDraft: false}, ...])`.
116
116
  4. New prepaid setups during this period (a new prepaid started this month): invoke `plan_recipe(recipe: 'prepaid-expense', ...)` (see the `prepaid-expense` recipe in the transaction-recipes skill) — this creates the bill + N future-dated DRAFT journals; the current period's journal is then in the bulk_finalize_drafts queue above.
@@ -141,7 +141,7 @@ On first month of FY only — engine creates the scheduler and posts the first a
141
141
 
142
142
  For each active loan capsule (via `search_capsules(filter: {status: {eq: 'ACTIVE'}})` (capsule type is not filterable — see `building-blocks.md` § Filter limits)):
143
143
 
144
- 1. **STOP — do not select these with a filter.** Journals cannot be narrowed to one capsule: `JournalFilter` declares no `capsuleResourceId`, a journal row carries no capsule link even at `view: 'full'` or on `GET /journals/{id}`, and `GET /capsules/{id}` returns only `totalTransactions` a count (all measured 2026-09-07). A date+status search returns EVERY matching DRAFT in the org, including drafts a practitioner deliberately parked, so passing it to `bulk_update_journals(saveAsDraft: false)` finalizes unrelated work. Surface the capsule and its expected journal count and let the practitioner identify the journals to finalize.
144
+ 1. **STOP — do not select these with a filter.** Journals cannot be narrowed to one capsule by a filter: `JournalFilter` declares no `capsuleResourceId`, and `GET /capsules/{id}` returns only `totalTransactions`, a count (measured 2026-09-07). A date+status search returns EVERY matching DRAFT in the org, including drafts a practitioner deliberately parked, so passing it to `bulk_update_journals(saveAsDraft: false)` finalizes unrelated work. `get_journal` (`GET /journals/{id}`) does return the link as `capsule: {resourceId, type, title}` (measured 2026-09-23), so check each candidate journal's `capsule.resourceId` against this capsule, keep only the matches, and confirm the count with the practitioner before finalizing.
145
145
  2. Should return exactly one DRAFT journal per active loan. Each is a 3-line entry (debit Loan Payable, debit Interest Expense, credit Cash) with the correct amortization split for the period.
146
146
  3. Collect resourceIds, then `bulk_update_journals(items: [{resourceId: <id>, saveAsDraft: false}, ...])`.
147
147
  4. Do NOT post manual loan-interest accruals — the recipe already emitted the journal with the correct split per `clio calc loan` schedule.
@@ -118,7 +118,7 @@ plan_recipe(
118
118
  )
119
119
  ```
120
120
 
121
- Then `execute_recipe(...)`. Engine emits 2 journals: declaration (Dr Retained Earnings / Cr Dividends Payable, with optional withholding leg) and payment cash-out. Both attached to the dividend capsule. Both can be DRAFT or ACTIVE based on `finalize` flag.
121
+ Then `execute_recipe(...)`. Engine emits a declaration journal (Dr Retained Earnings / Cr Dividends Payable) and a payment cash-out, plus a withholding cash-out when `withholdingRate > 0`. Only the declaration journal follows the `finalize` flag (DRAFT or ACTIVE). The cash-outs post ACTIVE immediately, dated `paymentDate`: cash entries have no draft state. So run `execute_recipe` on the actual payment date, not at FY-end when the dividend is only declared. If the declaration must be booked in the FY-end close, post it alone with `create_journal` and record the payment with `create_cash_out` when the money leaves the account.
122
122
 
123
123
  For interim dividends declared during the year: those should already be posted in their respective monthly closes. Y3 covers FY-end final dividend only.
124
124
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 5.63.0
3
+ version: 5.64.0
4
4
  description: >-
5
5
  Use this skill when modeling complex multi-step accounting transactions —
6
6
  anything that spans multiple periods, involves changing amounts, or requires
@@ -77,22 +77,17 @@ If `topUpRequired` is below the entity's materiality threshold: skip the recipe
77
77
 
78
78
  ```
79
79
  plan_recipe(
80
- // Note: gl*, capsuleType, capsuleName, bankAccountResourceId, vendor, customer below are illustrative — auto-resolved at execute time from CoA, not real plan_recipe params.
81
80
  recipe: 'ecl',
82
- receivables: [
83
- {bucket: 'current', balance: 100000, lossRate: 0.005},
84
- {bucket: '30d', balance: 50000, lossRate: 0.02},
85
- {bucket: '60d', balance: 20000, lossRate: 0.05},
86
- {bucket: '90d', balance: 10000, lossRate: 0.10},
87
- {bucket: '120d+', balance: 5000, lossRate: 0.50}
81
+ buckets: [
82
+ {name: 'Current', balance: 100000, rate: 0.5},
83
+ {name: '1-30 days', balance: 50000, rate: 2},
84
+ {name: '31-60 days', balance: 20000, rate: 5},
85
+ {name: '61-90 days', balance: 10000, rate: 10},
86
+ {name: '91+ days', balance: 5000, rate: 50}
88
87
  ],
89
88
  existingProvision: 5000,
90
89
  currency: 'SGD',
91
- glAllowance: <resourceId of 'Allowance for Doubtful Debts' account>,
92
- glBadDebtExpense: <resourceId of 'Bad Debt Expense' account>,
93
- valueDate: '2025-12-31',
94
- capsuleType: 'ECL Provision',
95
- capsuleName: 'FY2025 Year-End ECL True-Up'
90
+ startDate: '2025-12-31' // provision date: the aged AR report date. The ECL journal is dated on it.
96
91
  )
97
92
  ```
98
93
 
@@ -113,6 +108,10 @@ If `Allowance for Doubtful Debts` doesn't exist in the CoA: `create_account(name
113
108
  execute_recipe(recipe: 'ecl', ...same args...) // accounts auto-resolved from CoA; pass `bankAccountName` / `contactName` for fuzzy resolve
114
109
  ```
115
110
 
111
+ `startDate` is required: it dates the ECL journal, so pass the aged AR report date (`2025-12-31` here).
112
+
113
+ When the calculated ECL equals `existingProvision` there is no adjustment: the plan has no steps and `execute_recipe` refuses with "Nothing to post" before creating anything.
114
+
116
115
  Returns: `{ capsule: {resourceId, type, title}, steps: [{step: 1, action: 'journal', status: 'created', resourceId: <journal id>}], summary: {total: 1, created: 1} }`. The single journal is DRAFT — finalize via `update_journal(resourceId: <id>, saveAsDraft: false)` once the practitioner confirms the inputs.
117
116
 
118
117
  ### Step 5 — Verify
@@ -83,7 +83,7 @@ execute_recipe(recipe: 'loan', ...same args...) // accounts auto-resolved from
83
83
  ```
84
84
 
85
85
  Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...], summary: {total, created, ...} }`. The recipe creates **termMonths + 1 entries upfront**:
86
- - Step 1: 1 cash-in for the loan disbursement (per `jaz-api/SKILL.md` rule 26: `accountResourceId` at top level for the bank account, `lines: [{accountResourceId: <Loan Payable>, amount: 100000}]` for the offset). Posted ACTIVE if `finalize: true` was passed; otherwise DRAFT.
86
+ - Step 1: 1 cash-in for the loan disbursement (per `jaz-api/SKILL.md` rule 26: `accountResourceId` at top level for the bank account, `lines: [{accountResourceId: <Loan Payable>, amount: 100000}]` for the offset). Posted ACTIVE immediately, whatever `finalize` says: cash entries have no draft state.
87
87
  - Steps 2..termMonths+1: **N future-dated DRAFT journals** (one per repayment period, dated end-of-month for each month from `<startDate>+1 month` through `<startDate>+termMonths`). Each is a 3-line entry: debit Loan Payable (principal portion per amortization schedule), debit Interest Expense (interest portion), credit Cash.
88
88
 
89
89
  All N journals attach to the same capsule. They sit DRAFT until you finalize them — typically one per month during monthly-close after the actual bank payment posts.
@@ -95,17 +95,19 @@ execute_recipe(recipe: 'dividend', ...same args...) // accounts auto-resolved f
95
95
 
96
96
  Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...], summary: {total: 2 or 3, created: 2 or 3} }`. The recipe creates 2 entries (or 3 with withholding), all attached to the same capsule:
97
97
  - Declaration journal (DRAFT or ACTIVE per `finalize` flag)
98
- - Payment cash-out (DRAFT the actual bank payment hasn't happened yet at recipe-execution time; finalize when payment leaves the account)
99
- - Withholding cash-out (DRAFT — same; finalize when the WHT remittance is made to tax authority)
98
+ - Payment cash-out: posted ACTIVE immediately, dated `paymentDate`. Cash entries have no draft state, so `finalize` does not apply and there is nothing to finalize later.
99
+ - Withholding cash-out: same, posted ACTIVE immediately, dated `paymentDate`.
100
100
 
101
- ### Step 5 Verify (after both finalized)
101
+ **Run the recipe on the actual payment date**, once the money has left the bank account. The declaration journal still carries `declarationDate`, so booking it late puts it in the right period. Running at declaration time instead posts a live bank payment that has not happened yet, which the bank reconciliation will not match until the real payment arrives. If the declaration must be booked before the payment (for example at FY-end), post the declaration alone with `create_journal` (Dr Retained Earnings / Cr Dividends Payable) and record the payment with `create_cash_out` when the money leaves the account.
102
+
103
+ ### Step 5 — Verify (after the declaration is finalized and the payment is posted)
102
104
 
103
105
  After declaration finalized (Dec 31, 2025):
104
106
  - `generate_balance_sheet(period_end: '2025-12-31')`.
105
107
  - Assert: `balance['Retained Earnings']` reduced by 200,000.
106
108
  - Assert: `balance['Dividends Payable']` increased by 200,000.
107
109
 
108
- After payment finalized (Mar 15, 2026):
110
+ After payment posted (Mar 15, 2026):
109
111
  - `generate_balance_sheet(period_end: '2026-03-15')`.
110
112
  - Assert: `balance['Dividends Payable']` is now 0.
111
113
  - Assert: `balance['Cash']` reduced by 200,000 (or 180,000 if withholding).
@@ -148,6 +150,6 @@ After payment AND WHT remittance:
148
150
  ## Cross-references
149
151
 
150
152
  - Year-end close (Y3 in year-end-close) — final FY dividend declaration AFTER the FY's audited net profit is determined. The declared amount and withholding rate drive the recipe inputs.
151
- - Month-end close — interim dividends declared mid-year are posted in the month they were declared. Recipe runs once at declaration; payment cash-out finalizes when the actual bank disbursement happens (typically next month).
153
+ - Month-end close — interim dividends declared mid-year are posted in the month they were declared. The payment cash-outs post ACTIVE the moment the recipe runs, so run it once the bank disbursement has happened (typically next month), or book the declaration alone with `create_journal` in the declaration month and record the payment with `create_cash_out` when it is made.
152
154
  - `audit-prep.md` step 8 — auditor reviews `generate_equity_movement` to verify dividends are correctly classified as equity reduction (not P&L expense).
153
155
  - `statutory-filing.md` — SG Form C-S Box 12 (dividends paid during YA) reads from this capsule's payment cash-out entries.
@@ -6,7 +6,7 @@
6
6
 
7
7
  ### Recipe engine entry point
8
8
  - **`plan_recipe(recipe: 'fixed-deposit', ...)`** — used in step 2: returns RecipePlan with placement + N accrual + maturity steps.
9
- - **`execute_recipe(recipe: 'fixed-deposit', ...)`** — used in step 4: posts the placement cash-out (today), N future-dated DRAFT accrual journals (one per period), and maturity cash-in (dated termMonths later, also DRAFT).
9
+ - **`execute_recipe(recipe: 'fixed-deposit', ...)`** — used in step 4: posts the placement cash-out (today), N future-dated DRAFT accrual journals (one per period), and maturity cash-in (dated termMonths later, posted ACTIVE immediately: cash entries have no draft state).
10
10
 
11
11
  ### Calculator (cross-check, no API key needed)
12
12
  - **`clio calc fixed-deposit --principal <p> --rate <annual %> --term <months> --start-date <YYYY-MM-DD> --currency <code> [--compound monthly|annually] --json`** — used in step 1: compute monthly accrual amounts. Default simple interest; `--compound` for compound interest. Returns `{ totalInterest, schedule[n] }` where each row carries `period`, `accrualDate`, `accrualAmount`, `accruedToDate`, `journal`.
@@ -83,7 +83,7 @@ Bank account: resolve `bankAccountResourceId` for the disbursement bank (where t
83
83
  execute_recipe(recipe: 'fixed-deposit', ...same args...) // accounts auto-resolved from CoA; pass `bankAccountName` / `contactName` for fuzzy resolve
84
84
  ```
85
85
 
86
- Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...14], summary: {total: 14, created: 14} }`. The recipe creates 14 entries upfront: 1 placement cash-out (immediately ACTIVE if `finalize: true`), 12 future-dated DRAFT accrual journals, 1 future-dated DRAFT maturity cash-in (dated `startDate + termMonths`).
86
+ Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...14], summary: {total: 14, created: 14} }`. The recipe creates 14 entries upfront: 1 placement cash-out (ACTIVE immediately, whatever `finalize` says), 12 future-dated DRAFT accrual journals, 1 future-dated maturity cash-in (dated `startDate + termMonths`, also ACTIVE immediately: cash entries have no draft state).
87
87
 
88
88
  ### Step 5 — Monthly action (during monthly-close)
89
89
 
@@ -31,7 +31,7 @@
31
31
 
32
32
  ### Engine entry points (DO NOT INVOKE in normal operation)
33
33
  - ~~`plan_recipe(recipe: 'fx-reval', ...)`~~ — engine still accepts this for legacy reasons; output is for inspection only.
34
- - ~~`execute_recipe(recipe: 'fx-reval', ...)`~~ **double-posts. Never invoke in a production org.**
34
+ - ~~`execute_recipe(recipe: 'fx-reval', ...)`~~ (and `clio ct fx-reval` without `--plan`): **refused.** Executing would double-post, so the engine rejects the call before it creates anything.
35
35
 
36
36
  ### Cross-references
37
37
  - Operational context: invoked during month-end close only as a VERIFICATION step (cross-check Jaz's auto-posted reval against an independent calculation; surface any variance). Same during the GST/VAT filing cycle and year-end close.
@@ -130,9 +130,9 @@ This file feeds `audit-prep.md` step 8 supporting schedules. Auditors love indep
130
130
 
131
131
  ---
132
132
 
133
- ## Why the engine still accepts the recipe
133
+ ## Why the engine still plans the recipe
134
134
 
135
- Historical: pre-platform-auto-FX-reval orgs needed this. Some orgs may still run on a configuration where auto-FX is disabled (rare, legacy). For those orgs, `execute_recipe(recipe: 'fx-reval', ...)` posts the manual reval per the prior version of this recipe (period-end journal + Day 1 reversal). DO NOT use this path in any modern org.
135
+ Historical: pre-platform-auto-FX-reval orgs needed this. Some orgs may still run on a configuration where auto-FX is disabled (rare, legacy). `execute_recipe(recipe: 'fx-reval', ...)` is refused for every org, so for those orgs take the period-end journal and Day 1 reversal from `plan_recipe(recipe: 'fx-reval', ...)` and post them as two manual journals with `create_journal` (the reversal dated the first day of the next period). DO NOT do this in any modern org.
136
136
 
137
137
  If you genuinely need to know whether auto-FX is enabled for a specific org: check organization settings via `get_organization()`. If the auto-FX flag is on (default and typical), this recipe is verification-only as documented above.
138
138
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ### Recipe engine entry point
8
8
  - **`plan_recipe(recipe: 'provision', ...)`** — used in step 2: returns RecipePlan with PV-recognition journal + N period unwinding journals + settlement cash-out.
9
- - **`execute_recipe(recipe: 'provision', ...)`** — used in step 4: posts initial PV journal (today), N future-dated DRAFT discount-unwinding journals (one per month), and settlement cash-out (dated `settlementDate`, also DRAFT).
9
+ - **`execute_recipe(recipe: 'provision', ...)`** — used in step 4: posts initial PV journal (today), N future-dated DRAFT discount-unwinding journals (one per month), and settlement cash-out (dated `settlementDate`, posted ACTIVE immediately: cash entries have no draft state).
10
10
 
11
11
  ### Calculator (cross-check, no API key needed)
12
12
  - **`clio calc provision --amount <undiscounted total> --rate <annual %> --term <months> --start-date <YYYY-MM-DD> --currency <code> --json`** — used in step 1: compute PV at recognition + per-period unwinding charge. Returns `{ presentValue, totalUnwindingCharge, schedule[n] }` where each row has `period`, `openingProvision`, `unwindingCharge`, `closingProvision`.
@@ -83,7 +83,7 @@ Bank account: only needed for the settlement cash-out at the end of the term.
83
83
  execute_recipe(recipe: 'provision', ...same args...) // accounts auto-resolved from CoA; pass `bankAccountName` / `contactName` for fuzzy resolve
84
84
  ```
85
85
 
86
- Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...62], summary: {total: 62, created: 62} }`. Initial recognition journal (today, ACTIVE if `finalize: true`); 60 future-dated DRAFT unwinding journals; 1 future-dated DRAFT settlement cash-out.
86
+ Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...62], summary: {total: 62, created: 62} }`. Initial recognition journal (today, ACTIVE if `finalize: true`); 60 future-dated DRAFT unwinding journals; 1 future-dated settlement cash-out, posted ACTIVE immediately (cash entries have no draft state).
87
87
 
88
88
  ### Step 5 — Monthly action (during monthly-close)
89
89