Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog.

[Unreleased]

[0.11.0] - 2026-10-13

  • Roadmap as forge issues (task-plus ADR 0004); release waits for the merge

Changed

  • ROADMAP.md follows task-plus ADR 0004: named epics of - #N text lines, each an open issue labelled roadmap on the Bytestone Forgejo; shipped items moved into this changelog under the versions that finished them. merge_wait: 10m in task-plus.yml, so a release's pull request is tagged and published in the same run once merged. CLAUDE.md states the code-flow level 1 practice: one branch per story, every change through a pull request.

[0.10.0] - 2026-10-13

[0.9.0] - 2026-10-13

  • Policy as code: a parameter derived by code, the Policy contract, easyaccess/v2 tracking the bank's savings rate (gobank story 1.8.9)

Added

  • Declaration.Algorithm, a parameter derived by code: a function of the parameters it reads through the reader on a day, answering in the declaration's kind, for the bank's policy to express lag and take (gobank ADR-0006, ADR-0009). Declaration.IsDerived is true of a formula or an algorithm. CheckDeclarations refuses a derived parameter that publishes a value, or one with both a formula and an algorithm.
  • Policy, a bank's own rules as versioned code (gobank ADR-0007): its bank-scoped declarations and start-up check; CheckPolicy. SavingsRateKey and LendingRateKey name the bank's rates, which a policy declares and a tracking version reads.
  • easyaccess/v2, Easy Access Tracker: the rate is the bank's savings_rate_bps less 15 bps, floor 0, read from the bank's policy; its golden runs a cut mid-month.
  • testkit: Reader, the parameter resolution alone over declarations and in-memory settings, which the Runner now reads through; SetMarket; CheckVersionWith, for a version whose start-up reads the bank.

Roadmap

  • Policy and Declaration.Algorithm, a parameter derived by code; easyaccess/v2 tracking the bank's savings rate (gobank story 1.8.9)

[0.8.0] - 2026-10-12

  • Posting.Step names a posting within its event (gobank ADR-0011); the day rule reads UTC days

Added

  • Posting.Step names a posting within the event the rule answered, so a runner records it against its cause, the account and the step and a replay finds the one already made (gobank ADR-0011). ApplyAccrued, the one posting the v1 rules make, names it apply.

Fixed

  • The day rule read a day in the zone it was given, so a runner handing an instant near midnight in another zone got positions keyed by a local midnight no UTC-keyed ledger would find, and a value time at the local day's end. A day is a UTC day (Facts.Day, go-luca's Position.Day): the rule now reads the UTC day of any instant it is given (issue #2, go-luca's cross-timezone regression).

Added

  • Tests ported from go-luca's retired interest suite (issue #2): a zero rate accrues and applies nothing; the cross-timezone day; BenchmarkInterestDay, the rule's cost per account-day with the facts in memory.

[0.7.0] - 2026-10-11

Fixed

  • overdraft/v1 read overdrawn as a negative balance and refused past -limit. In the bank's view (gobank ADR-0008) an overdrawn current account is a debit balance, positive like a loan, so the limit is the most the balance may rise to. Its golden was the customer's way about: the withdrawal credited the account and the interest ran from the account to Income:Interest. Regenerated (issue #1).

Changed

  • testkit.Runner posts in the bank's view whatever the family: Deposit credits the account and Withdraw debits it; Drawdown and Repay are the lending verbs. Lending tests that funded a loan with Deposit now draw it down, and their goldens say so.

Added

  • ScopeMarket and RiskFreeRateKey: a currency's official risk-free rate as a parameter at a scope of its own (gobank ADR-0009), declared by key alone and resolved by the runner for the currency of the product whose rules read it; written only by the market's feed.

[0.6.0] - 2026-10-10

[0.5.0] - 2026-10-10

[0.4.0] - 2026-10-10

  • Products as versioned code (gobank ADR-0006, story 1.8.1): the version contract, six v1 packages, the feature framework retired

Added

  • The product contract: Version (identity, declared parameters, rules), nine events (StartUp, Open, PrePosting, PostPosting, Day, ParameterChange, Command, ChangeOfVersion, Close), Facts read through an interface, Intents (settings, postings, positions) returned, Refusal for a rule's no. Rules are pure; the bank's runner carries the intents out.
  • Parameters as declarations with a scope (bank, version, account), a kind (bps, money, int, cycle, day, text), a published value, or a derivation from another parameter (spread, floor, cap). CheckDeclarations is the shared start-up check.
  • gbp.Interest, the interest-bearing rules a version embeds: daily accrual on the closing balance, application when the cycle ends, the apply-interest command, close and change of version applying to date.
  • Six version packages, each with its own golden: easyaccess/v1, fixedterm/v1 (maturity fixed at open from term_months, withdrawals refused before it), isa/v1 (deposits refused over the allowance), personalloan/v1, mortgage/v1, overdraft/v1 (movements refused past the limit). The rates the bank held as float64 are published basis points. The five 32-day goldens are byte-identical to the old framework's.
  • testkit.Runner, the reference runner over an in-memory go-luca ledger: implements Facts, resolves parameters by scope with effective-dated settings and derivations, carries out intents, surfaces refusals, logs reads. testkit.CheckVersion, testkit.RoundTrip.

Changed

  • Accrue and ApplyAccrued are functions of the engine, not methods of a product; ApplyAccrued takes the family and the description and always applies, the version deciding when. ApplicationCycle.Description exported.
  • go-luca pinned at v0.5.0.

Removed

  • Product, Feature and the handler interfaces, SimContext, Simulation, ManagedAccount, ParameterStore, Clock, the catalogue constructors (EasyAccess() and the rest), RepaymentSchedule (monthly_repayment was never set by the bank), NextDay, ParamInterestApplication; testkit's ScenarioBuilder, GolucaScenario and the FSMs. gobank stays on v0.3.0 until its story 1.8.2 adopts the contract.

Roadmap

  • The product contract and six v1 version packages (gobank ADR-0006, story 1.8.1): rules pure over facts, intents carried out by the runner, parameters declared with scope, kind and derivation; the feature framework and simulation engine retired
  • Golden .goluca tests per version, byte-identical to the framework's

[0.3.0] - 2026-10-05

  • Product.Accrue and Product.Apply split the day rule for gobank stage 3 story (e); RateBps exported

Added

  • Product.Accrue and Product.Apply, the two halves of the day rule (gobank ADR-0002 stage 3, story e). Accrue is the position at the end of a day with the day's interest accrued on the closing balance and nothing applied: pure and the same for any balance, so the daily pass writes it as the day's provisional projection and every event that moves the balance writes it again. Apply books the cycle-end application from a position, and applying an applied position posts nothing, so the pass may revisit an account after a restart. NextDay is unchanged and is Apply of Accrue.
  • RateBps, the conversion from a product's annual rate to the integer basis points the day rule accrues in, exported from the engine for callers that run the rules without it.

Roadmap

  • Accrue/ApplyAccrued: the day rule over go-luca positions, with the application cycle a parameter (gobank ADR-0002 stage 3)

[0.2.0] - 2026-10-05

  • Product.NextDay: the day rule as a pure function over go-luca positions; application cycle a product parameter; no separate month-end pass

  • Product.NextDay (gobank ADR-0002 stage 3, story c): the per-account day rule as a pure function — from the account's position at the end of the previous day (go-luca Position, balance and accrued interest as an exact fraction) and the day's closing balance to the position at the end of this day and the ledger Postings it calls for. Interest accrues on the closing balance; when the product's cycle ends on the day the whole minor units are applied and the remainder carries forward.

  • The application cycle is a product parameter, interest_application: daily, monthly (the default, so the catalogue is unchanged) or annual. InterestAccrual runs NextDay at EndOfDay and no longer handles EndOfMonth; interest application is inside the daily pass. EndOfMonth remains for repayment schedules.

  • AccountUpdate on an application day now has ClosingBalance and AccruedNumerator after application, InterestAmount the interest applied, and AccruedDelta the day's accrual alone. Callers that derived the applied interest from the cached balance should use InterestAmount.

  • Requires go-luca v0.3.0 (Position, Fraction) and go-postgres v0.7.0.

  • Simulation.AdoptAccount: put an account that already exists in the ledger under the engine's management (status, opened date, cached balance and parameters as the caller read them) without creating it or raising an AccountOpened event. A bank restarting over a stored ledger brings its accounts back this way. Rate resolution is shared with OpenAccount.

[0.1.10] - 2026-08-26

  • Exact daily interest accrual in memory with monthly application; cached balances; PaceHook

[0.1.10] - 2026-08-26

  • Rework InterestAccrual: exact integer daily accrual in memory (numerator over 10,000 x 365, remainder carried — sub-penny interest is no longer lost), applied to the account monthly as a single ledger movement. Previously interest posted daily to :Accrued sub-accounts and was never applied to balances.
  • ManagedAccount gains CachedBalance (maintained by RecordMovement, removing per-day balance queries from end-of-day sweeps), RateBps, AccruedNumerator, and AccruedInterest().
  • Simulation gains RefreshBalances (post-import cache priming) and PaceHook (lets single-threaded hosts yield during account sweeps).
  • AccountUpdate gains AccruedDelta and AccruedNumerator.

[0.1.9] - 2026-08-24

  • Bump go-luca to v0.2.31 (with go-postgres v0.5.5, gotreesitter v0.6.8); the previous go-luca v0.2.25 pin was unresolvable under the git.bytestone.uk module path

[0.1.6] - 2026-03-25

  • Fix transaction directions and move interest logic to product layer

[0.1.5] - 2026-03-24

  • Migrate dependencies from github.com/drummonds to codeberg.org/hum3

[0.1.4] - 2026-03-19

  • working on interest feature

[0.1.3] - 2026-03-19

  • updating checks after go-postgres update

[0.1.2] - 2026-03-17

  • adding documentation

[0.1.1] - 2026-03-17

  • Working on goluca integration

[0.1.0] - 2026-03-16

  • fixing linter bug

Added

  • Core types: EventType, Feature interface, Product, ManagedAccount, Clock, ParameterStore
  • Simulation engine with event dispatch and time advancement
  • Features: StatusLifecycle, InterestAccrual, DepositAcceptance, WithdrawalProcessing, TermLock, ISAWrapper, OverdraftFacility, RepaymentSchedule
  • Product catalog: EasyAccess, FixedTerm, ISA, PersonalLoan, Mortgage, Overdraft
  • Test infrastructure: testkit package with ScenarioBuilder, goluca assertions, golden file helpers