ENGINE 27

SFS-E27-INT · DEBT SERVICE · REV 2026-07-24 · PRODUCTION

PRODUCTION = runnable end-to-end, CI-backed, full test suite passing. All data fictional and seeded.

Interest Accrual & Loan Amortization

Does the interest a note reports equal the interest its own terms produce?

A lender carries a portfolio of promissory notes, and every period somebody accrues interest on each one, rolls its balance forward, and posts a journal entry that is supposed to tie to both sides. This engine proves, note by note, that the interest a schedule reports is the interest its own terms produce. It reads the seven artifacts a debt-service record carries -- the note register, the roll-forward schedule, the interest journal, the trial balance, the interest summary, the maturity watchlist and the debt-service report -- and runs twenty-eight deterministic controls over them. Is the loan file complete and the review date legible, are the note ids unique and each note's rate, day-count and principal well-formed with two distinct sides, does every schedule row carry its key fields and tie its day count to its own span, does each period's accrued interest re-derive from beginning balance times rate times days over the day-count year, does every ending balance rebuild from beginning plus advance plus accrued minus payment and continue as the next period's beginning, does the schedule open at the note's principal, does each period accrue at the stated or stepped default rate, does accrual stop at maturity, is a prepayment-barred note left alone and a subordinate kept behind its senior, no payment beyond the balance owed and a payoff settled to zero, do the reciprocal receivable and payable and the interest income and expense each tie the schedule, does the journal prove to zero and tie the accrual and cover every note, and do the settled counts and the maturity watchlist recompute from the schedule beneath them. It rebuilds every figure through one shared accrual kernel, compares with exact ==, measures every maturity and gate test to the as_of review date rather than the system clock, and never writes to a source artifact.

An amortization schedule is the most trusted column on the debt-service worksheet: a beginning balance, a rate, an accrual, an ending, repeated down the page. That is exactly why it drifts. None of the ways it goes wrong look wrong in a column of balances. A period accrues at the wrong rate, on the wrong balance, or over the wrong day count, and the booked interest is off by an amount too small to notice against a six-figure balance -- until it compounds. One period's ending is not the next period's beginning, an advance is added but never lands, a payment relieves more than was owed, or the payoff leaves a residual cent instead of settling to zero. A gate is missed -- interest keeps accruing past maturity, the default rate never steps after an event of default, a note barred from prepayment is paid down early, a subordinated note is paid ahead of the senior it sits behind. And the two sides quietly stop reconciling -- the receivable and the payable drift apart, interest income no longer equals interest expense, the monthly journal no longer ties the accrual it was meant to record. The engine rebuilds every accrual and every balance from the note's own terms through one shared kernel and compares with exact ==, enforces each gate as a date or a threshold rather than a judgement, ships a planted-defect file for every registered control, and states its benchmark as a command rather than a claim. A cent of drift is invisible until someone recomputes the interest the note already booked.

Run it Book a consultation

Architecture

functional block stack · static overview
Structural Precondition Note Register Accrual Re-Derivation Roll-Forward Rate, Maturity & Prepayment Gates Payment Waterfall & Subordination Reciprocal Reconciliation Journal & Trial-Balance Rollup Recompute substrate: seeded fictional data

Select a layer

Nine control families run in registry order over each loan file. The first proves the others had something to read and that the review date is legible, because a control that passes on absent evidence reports assurance it never performed.

Key specifications

at a glance
10,4021
Tests
the collected checks that pin this engine's behavior
282
Controls
independent rules run over every loan file
93
Families
groups of controls, each answering one question
304
Loan files
one clean baseline plus twenty-nine planted-defect files
75
Artifacts read
note register, roll-forward schedule, interest journal, trial balance, interest summary, maturity watchlist, debt-service report
06
Tolerance
a cent off the re-derivation is a break, not a rounding band

What it does for you

plain terms

A lender carries notes receivable and interest income; the reciprocal borrower carries notes payable and interest expense; and every period somebody accrues interest, rolls the balance forward, and posts a journal entry that is supposed to tie to both sides. The question is narrower and entirely arithmetic: rebuild each note's accrual from its stated rate and day-count, roll the balance forward row by row, and prove the schedule, the reciprocal GL and the journal all agree. Four failures hide inside that, and none of them look wrong in a column of numbers. The accrual drifts from the terms -- a period accrues at the wrong rate, on the wrong balance, or over the wrong day count, and the booked interest is off by an amount too small to notice against a six-figure balance until it compounds. The roll-forward breaks -- one period's ending is not the next period's beginning, an advance is added but never lands, a payment relieves more than was owed, the payoff leaves a residual cent instead of settling to zero. A gate is missed -- interest keeps accruing past maturity, the default rate never steps after an event of default, a note barred from prepayment is paid down early, a subordinated note is paid ahead of the senior it sits behind. And the two sides stop reconciling -- the receivable and the payable drift apart, interest income no longer equals interest expense, the monthly journal no longer ties the accrual it was meant to record. None of these are judgment calls. They are equalities and date gates -- an accrual against beginning times rate times days with exact ==, an ending against its re-derivation, a period start against a maturity date, a payment against the period's accrued interest -- which a deterministic control settles better than a schedule inherited by whoever tracks the portfolio this year.

The seeded demo runs every control over every loan file7. The CLI generates thirty fictional loan files -- one clean baseline and one carrying each of the twenty-nine planted defects -- then runs all twenty-eight controls over each. The clean file returns PASS with no flags; every defect file returns REVIEW or FAIL and names the control it tripped along with the reason that control exists.

The day-count and the accrual are independent controls8. A schedule can accrue interest that re-derives perfectly and still be wrong, because it re-derives against the very day count that is wrong. One defect file stretches a period's end date without touching its stated day count: acc_interest_rederives still passes -- the accrual matches the stated day count -- while acc_day_count_ties fails, because the stated day count no longer equals the span between the period's own two dates. The two controls catch different failures at the same boundary.

The accrual is recomputed, not read back9. A defect file adds ten dollars to one period's stated accrued figure without changing the note's rate, balance or day count. acc_interest_rederives rebuilds the interest from beginning times rate times days through the shared kernel and fails on the cent, proving the accrued figure is recomputed from the terms beneath it rather than trusted as stated.

Functional block diagram

engineering · each block links to its source
Functional block diagram of the Interest Accrual & Loan Amortization engine loan files complete complete complete complete complete complete complete complete resolves resolves resolves resolves resolves resolves resolves resolves accrued total findings findings findings findings findings findings findings Seeded Loan Files StructuralPrecondition Note Register Accrual Re-Derivation Roll-Forward Rate, Maturity &Prepayment Gates Payment Waterfall& Subordination ReciprocalReconciliation Journal &Trial-Balance Rollup Recompute Register, Schedule& Trial Balance Verdict and Findings SFS-E27-INT · FUNCTIONAL BLOCK DIAGRAM · REV 2026-07-24

Plain terms

  • Seeded Loan Files. fictional loan files enter the control registry
  • Structural Precondition. is the loan file complete and the review date legible
  • Note Register. is the register sound and each note's terms well-formed
  • Accrual Re-Derivation. does each period's interest re-derive from the terms
  • Roll-Forward. does the balance roll forward, begin to end
  • Rate, Maturity & Prepayment Gates. do the rate, maturity and prepayment gates hold
  • Payment Waterfall & Subordination. does the payment waterfall and subordination order hold
  • Reciprocal Reconciliation. do the two sides of each note reconcile
  • Journal & Trial-Balance. does the journal balance, tie the accrual, and cover every note
  • Rollup Recompute. do the rollups recompute from the schedule
  • Register, Schedule & Trial Balance. the seven artifacts and the accrual kernel every rule resolves against
  • Verdict and Findings. every finding, with the reason it exists, ends at a person

Engineering

  • Seeded Loan Files. generate_corpus writes one clean baseline plus one planted-defect file for every named control, thirty files in total. Only each note's terms and the schedule events -- the period dates, advances and payment intent -- are stated; every accrual and ending balance, each note's settled flag and accrued total, the reciprocal trial balance, the interest journal, the maturity watchlist and the portfolio counts are re-derived through the same accrual kernel the engine later recomputes with, so the relationships the engine tests are the same relationships that produced the data.
  • Structural Precondition. One control. FAILs a file missing any of the seven artifact types or carrying a duplicate, or an unreadable as_of review date, so no downstream rule holds vacuously on absent evidence.
  • Note Register. Four controls: unique note ids, the required term and GL-account fields present, a well-formed rate / day-count / principal, and a lender and borrower that are two distinct entities.
  • Accrual Re-Derivation. Three controls: every row carries its key fields, its day count ties its own span, and its accrued interest rebuilds as beginning times rate times days over the day-count year, truncated to the cent and compared with exact ==.
  • Roll-Forward. Four controls: every row names a real note, each ending rebuilds from beginning + advance + accrued - payment, each ending is the next beginning, and the first period opens at the note's principal.
  • Rate, Maturity & Prepayment Gates. Four controls: each period accrues at the stated or stepped default rate, no interest accrues on or after maturity, a prepayment-barred note is not paid down early, and an unsettled note maturing inside the lead-time window is flagged.
  • Payment Waterfall & Subordination. Three controls: no payment exceeds the balance owed in its period, a payoff-expected note settles to exactly zero, and a subordinate does not reduce principal while its senior is unrepaid.
  • Reciprocal Reconciliation. Two controls: the note's receivable equals its payable equals its schedule ending, and its interest income equals its expense equals its re-derived accrual.
  • Journal & Trial-Balance. Four controls: the interest journal proves to zero, its booked interest ties the re-derived accrual, every note's GL accounts appear on the trial balance, and every trial-balance account maps back to a note.
  • Rollup Recompute. Three controls: each note's settled flag and accrued total recompute from its schedule, the portfolio settled / outstanding counts tie the summary, and the maturity watchlist ties the note evidence.
  • Register, Schedule & Trial Balance. The note register carries each note's id, sides, principal, rate, day-count, maturity, default-event, prepayment and subordination terms and its four GL account references; the roll-forward schedule carries each period's begin / advance / accrued / pay / end row; the trial balance carries the reciprocal receivable, payable, income and expense balances; and the interest summary, maturity watchlist, interest journal and debt-service report carry the derived determinations and counts. Every accrual, roll-forward, gate, waterfall, reciprocal, journal and rollup control resolves against these through the shared accrual kernel.
  • Verdict and Findings. Findings roll up per loan file: any FAIL is FAIL, FLAGs without FAILs are REVIEW, clean is PASS. The CLI exit code is the verdict, so a pipeline can gate on it. Reports carry no timestamps or absolute paths, which is what makes the committed report diffable.

Instruction set

every public command
CommandOperationOutputExitArtifacts
python run.py
regenerate the seeded fictional corpus, run all twenty-eight controls, write both reportsper-file verdicts and every actionable finding, then the overall verdict2 for the bundled corpus, which carries a planted defect for every control by designinterest_report.json, interest_report.md, samples/
python -m interest_engine samples
analyze an existing folder of loan files without regenerating itper-file verdicts and findings0 PASS / 1 REVIEW / 2 FAIL / 3 usagenone unless --json or --md is passed
python -m interest_engine samples --generate --quiet
regenerate the corpus and print only the overall verdicta single verdict line0 PASS / 1 REVIEW / 2 FAIL / 3 usagesamples/
python -m pytest interest_engine/tests -q
run the full test suite for this engine10402 passed0 when every test passesnone

Benchmarks

measured demo results
MeasureResult
Engine tests1010,402 tests collected
live collection from the engine directory
Registered controls1128 controls
counted from the registry, not from documentation
Planted defects1229 defect files
every registered control has at least one file that trips it
Control coverage13100 percent of controls with a planted defect
no control ships without a file demonstrating it firing

Control characteristics

engineering

Plain terms

The engine has no write path and therefore no autonomy to gate. It produces findings; a person decides whether a note is carried into the close and whether a maturing note is refinanced or repaid.

Engineering

Deterministic envelope. seeded fictional inputs, read-only operation, offline default mode.

Demo gate. FAIL on the bundled corpus, by design -- it carries a planted defect for every registered control

SeverityVerdictAction
No findings above PASSPASScarry the mechanically clean loan file into the documented sign-off and debt-service review
One or more FLAG, no FAILREVIEWa human resolves each flag; a note maturing inside the lead-time window and a maturity watchlist that does not tie the evidence both land here
One or more FAILFAILthe note or figure is not carried into the close until the failing control is cleared -- an accrual that does not re-derive, a broken roll-forward, a missed gate, a reciprocity break, or a rollup that does not recompute

Operating limits

what it refuses to do

See it run

control architecture
Corporate roll-forward architecture for Interest Accrual & Loan Amortization: opening state and period activity pass through deterministic controls to ending balances and an independent reporting tie-out.
The Interest Accrual & Loan Amortization engine tile states the question that has to hold note by note: every period accrues the interest its own rate, day-count and balance produce, the balance rolls forward begin to end, the maturity, rate-step, prepayment and subordination gates hold, and the reciprocal receivable / payable, interest income / expense and monthly journal all tie to the schedule beneath them.

Integration

how to run it

Distribution: public repository, MIT license.

python run.py
python -m pytest interest_engine/tests -q
Source on GitHub Engine README All tests, by engine Run in Codespaces
Substantiation
  • 1python -m pytest --collect-only -q -o addopts= (interest-accrual-automation/)
  • 2len(interest_engine.engine.REGISTRY)
  • 3distinct rule-id prefixes in interest_engine.engine.REGISTRY
  • 4samples/*.json after python run.py
  • 5len(interest_engine.model.DOC_TYPES)
  • 6interest_engine/tests boundary tests on the cent and the maturity / rate-step date
  • 7python run.py (interest-accrual-automation/); interest_engine/tests planted-defect suite
  • 8interest_engine/generate.py DEFECTS day_count_mismatch / accrual_wrong
  • 9interest_engine/generate.py DEFECTS accrual_wrong -> acc_interest_rederives
  • 10python -m pytest --collect-only -q -o addopts= (interest-accrual-automation/)
  • 11len(interest_engine.engine.REGISTRY)
  • 12len(interest_engine.generate.DEFECTS)
  • 13interest_engine/tests: every registered rule has a planted defect
  • 14interest-accrual-automation/README.md and interest_engine read-only / determinism tests
  • 15interest_engine/model.py DOC_TYPES
  • 16interest_engine/engine.py note_ and gate_ families
  • 17interest_engine/engine.py rf_ and recip_ families
  • 18interest_engine/generate.py

Show us where the hours go.

One conversation: you describe the work that consumes your team's month; we tell you plainly what this engine can take over, what it can't, and what a scoped first phase would cost. Your people keep approval authority.

Book a free consultation