ENGINE 41
SFS-E41-DTS · DEBT SIZING · REV 2026-07-24 · PRODUCTION
PRODUCTION = runnable end-to-end, CI-backed, full test suite passing. All data fictional and seeded.
Debt Term-Sheet Sizing & Loan Terms
Is the loan sized within the advance rate, and does every other term re-derive?
A borrower runs several projects through legal at once, and each period a new dated workbook lays the deals out side by side -- max loan amount, max loan-to-cost, the index-plus-spread rate build-up over a floor, the letter-of-credit and overdraft facilities, the origination / commitment / unused / admin fees, the term and its extension options, the recourse. A term sheet is a one-page summary re-keyed from a longer credit memo, and it drifts in ways nobody re-foots. This engine reads the seven artifacts that workbook emits -- the deal register, the rate build-up, the fee schedule, the rate summary, the fee summary, the expiry watchlist and the portfolio rollup -- and runs twenty-one deterministic controls over them. Is the sizing file complete and the pricing date readable, are the deal ids unique and the loan type, index and recourse each on their allowed list with a cost basis and advance rate declared, is the max loan within max loan-to-cost times the cost basis, is the drawn balance within the commitment, does the stated maturity reconcile to base term plus every extension option, does each deal carry exactly one rate build-up and does its all-in rate recompute as the greater of index-plus-spread and the floor, is every fee line well-typed and struck on a known base against a deal in the register with an amount that recomputes from that base and rate, is every term sheet still live at the pricing date and which are due to expire inside the lead-time window, and do the rollup's deal count, total commitment and total fees foot the deals while the expiry watchlist ties the term sheets actually due. It compares every figure with exact ==, caps the loan with exact <=, measures every expiry against the pricing date the file carries rather than the system clock, and never writes to a source artifact.
A term sheet is the tidiest page in the credit file: four deals side by side, a rate build-up under each, a fee schedule beneath that, and a rollup at the foot. That is exactly why it drifts. None of the ways it goes wrong look wrong in a tidy grid. The stated max loan creeps a cent or a million above max loan-to-cost times the cost basis, and the over-advance rides in the workbook while the cap sits in a cell nobody recomputed. A build-up figure is re-keyed rather than re-derived -- the all-in rate typed as index plus spread when the floor actually governs, or a fee struck on the commitment where it should have been struck on the undrawn balance -- so the number is arithmetically wrong against a rate that is entirely right. And the rollup is maintained beside the side-by-side rather than footed from it, so the total commitment, the total fees and the count of active deals drift the first time a deal moves and nobody re-adds the column. This engine rebuilds each of those figures from the base terms through the same kernel that produced them, compares with exact == and caps with exact <=, ships a planted-defect file for every registered control, and states its benchmark as a command rather than a claim. It is the sizing tie-out, run before anything closes: whether a draw is actually advanced against a budget line is a funding question a separate engine owns, and that engine explicitly declines to re-foot a term sheet.
Architecture
functional block stack · static overviewSelect a layer
Seven control families run in registry order over each sizing file. The first proves the others had something to read and that the pricing date is legible, because a control that passes on absent evidence reports assurance it never performed.
Key specifications
at a glanceWhat it does for you
plain termsA borrower runs several projects through legal at once, and each period a new dated workbook is produced from one template as the deals move toward close. It lays the deals out side by side -- max loan amount, max loan-to-cost, the index-plus-spread rate build-up over a floor, the letter-of-credit and overdraft facilities, the origination / commitment / unused / admin fees, the term and its extension options, the recourse -- and the question is narrow and entirely arithmetic: do the numbers on the term sheet reconcile to the terms the term sheet states? A term sheet is a one-page summary re-keyed from a longer credit memo, and three failures hide inside it, none of which look wrong in a tidy grid. The loan is sized over the advance rate: the stated max loan creeps a cent or a million above max loan-to-cost times the cost basis, and the over-advance rides in the workbook while the cap sits in a cell nobody recomputed. A build-up figure is re-keyed rather than re-derived: the all-in rate typed as index plus spread when the floor actually governs, or a fee struck on the commitment where it should have been struck on the undrawn balance, so the amount is wrong against a rate that is right. And the rollup drifts from the deals: the total commitment, the total fees or the count of active deals is maintained beside the side-by-side rather than footed from it, and it drifts the first time a deal moves and nobody re-adds the column. None of these are judgment calls. They are equalities and comparisons -- a loan against a recomputed cap with exact <=, an all-in rate against max(index + spread, floor), a fee against the truncating basis-point product of its rate and its correct base, a stated total against its re-derivation -- which a deterministic control settles better than a template inherited by whoever maintains the workbook this period.
The seeded demo runs every control over every sizing file7. The CLI generates twenty-three fictional sizing files -- one clean baseline and one carrying each of the twenty-two planted defects -- then runs all twenty-one 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 advance-rate cap is met at the cent8. Deal DL-203 is seeded sized exactly at its advance-rate cap, so the cap is proved to be met rather than merely cleared. One defect file re-sizes DL-204 to one cent above max loan-to-cost times its cost basis and re-derives the rollup, so the only control that sees it is size_ltc_not_exceeded -- the over-advance is one cent, the finding is unambiguous, and nothing else in the file moves.
The floor is exercised, not assumed9. Deal DL-202's build-up lands below its floor -- index plus spread is 600 basis points against a 700 basis-point floor -- so the max in the all-in rate genuinely governs on the clean baseline. A defect file then contradicts DL-201's stated all-in rate without touching its build-up, and rate_all_in_recomputes rebuilds the rate from index, spread and floor and fails, proving the rate is recomputed rather than read back.
Functional block diagram
engineering · each block links to its sourcePlain terms
- Seeded Sizing Files. fictional term-sheet workbooks enter the control registry
- Structural Precondition. is the sizing file complete and the pricing date legible
- Deal Register. is the register sound and every governing term on its list
- Sizing Constraints. is the loan within the advance rate and the maturity reconciled
- Rate Build-Up. does the all-in rate recompute from index, spread and floor
- Fee Schedule. is every fee struck on its correct base and does it recompute
- Term-Sheet Currency. is the term sheet still live and what is due to expire
- Portfolio Rollup. does the rollup foot the deals and tie the watchlist
- Register, Schedule & Rollup. the artifacts and the pricing date every rule resolves against
- Verdict and Findings. every finding, with the reason it exists, ends at a person
Engineering
- Seeded Sizing Files. generate_corpus writes one clean baseline plus twenty-two planted-defect files, twenty-three in total -- at least one file for every registered control, with a malformed-amount file added on the over-advance rule to exercise the integer-cents guard. Only the deals, the rate build-ups and the fee lines are stated as base facts; each deal's all-in rate, each fee line's amount, the expiry watchlist and the portfolio count and totals are re-derived through the same term-sheet 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, and FAILs an unreadable as_of pricing date, so no downstream rule holds vacuously on absent evidence.
- Deal Register. Five controls: unique deal ids, a known loan type, a known reference index, a known recourse structure, and a positive cost basis and max loan-to-cost for the advance-rate control to size against.
- Sizing Constraints. Three controls: max loan <= max loan-to-cost times cost basis recomputed with truncating basis-point math, drawn balance within the commitment, and stated maturity equal to base term plus every extension option.
- Rate Build-Up. Two controls: exactly one build-up row per deal, and the stated all-in rate equal to max(index + spread, floor) rebuilt through the shared kernel and compared at the basis point.
- Fee Schedule. Four controls: a known fee type, a known base of commitment / drawn / undrawn / facility, a deal in the register to attribute the line to, and an amount equal to the truncating basis-point product of the rate and that base.
- Term-Sheet Currency. Two controls: a FAIL for a term sheet lapsed on or before the pricing date, and a FLAG for one expiring inside the file's own lead-time window, which is still live but has to close before it lapses.
- Portfolio Rollup. Four controls: the active-deal count ties the register, the total commitment foots the deals' max loan amounts, the total fees foots the priceable fee lines, and the expiry watchlist is rebuilt from the deals and tied entry by entry.
- Register, Schedule & Rollup. The deal register carries each deal's id, project, loan type, index name, recourse, cost basis, max loan-to-cost, max loan amount, drawn balance, facility limit, base term, extension options, stated maturity and term-sheet expiration; the rate build-up carries the index, spread and floor behind each all-in rate; the fee schedule carries each line's type, base and rate; the rate summary, fee summary, expiry watchlist and term-sheet rollup carry the derived figures the controls re-derive; and the file itself carries the as_of pricing date and the commitment lead-time window. Every sizing, rate, fee, expiry and rollup control resolves against these through the shared term-sheet kernel.
- Verdict and Findings. Findings roll up per sizing 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| Command | Operation | Output | Exit | Artifacts |
|---|---|---|---|---|
python run.py | regenerate the seeded fictional corpus, run all twenty-one controls, write both reports | per-file verdicts and every actionable finding, then the overall verdict | 2 for the bundled corpus, which carries a planted defect for every control by design | sizing_report.json, sizing_report.md, samples/ |
python -m sizing_engine samples | analyze an existing folder of sizing files without regenerating it | per-file verdicts and findings | 0 PASS / 1 REVIEW / 2 FAIL / 3 usage | none unless --json or --md is passed |
python -m sizing_engine samples --generate --quiet | regenerate the corpus and print only the overall verdict | a single verdict line | 0 PASS / 1 REVIEW / 2 FAIL / 3 usage | samples/ |
python -m pytest sizing_engine/tests -q | run the full test suite for this engine | 10338 passed | 0 when every test passes | none |
Benchmarks
measured demo results| Measure | Result |
|---|---|
| Engine tests10 | 10,338 tests collected live collection from the engine directory |
| Registered controls11 | 21 controls counted from the registry, not from documentation |
| Planted defects12 | 22 defect files every registered control has at least one file that trips it |
| Control coverage13 | 100 percent of controls with a planted defect no control ships without a file demonstrating it firing |
Control characteristics
engineeringPlain terms
The engine has no write path and therefore no autonomy to gate. It produces findings; a person decides whether a deal is re-sized, whether a rate or fee is corrected, and whether a term sheet is chased before it lapses.
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
| Severity | Verdict | Action |
|---|---|---|
| No findings above PASS | PASS | carry the mechanically clean sizing file into the documented credit and legal review |
| One or more FLAG, no FAIL | REVIEW | a human resolves each flag; a term sheet expiring inside the lead-time window and an expiry watchlist that does not tie the deals both land here |
| One or more FAIL | FAIL | the deal or figure is not carried forward until the failing control is cleared -- an over-advance, a draw past its commitment, a maturity that does not reconcile, a rate or fee that does not recompute, a term off its allowed list, a lapsed term sheet, or a rollup total that does not foot |
- Read-only: sizing files are parsed and never written back, so the engine cannot introduce the break it reports.
- The engine is the sizing tie-out, not the funding decision; it moves no money and advances no draw against a budget line.
- Integer cents and integer basis points throughout, compared with exact ==; there is no tolerance band, and a figure a cent or a basis point off the re-derivation is reported as a break.
- The advance-rate cap is recomputed with truncating basis-point math and compared with exact <=, so a loan one cent above the cap fails and a loan sized exactly at the cap passes.
- A figure that should be integer cents or integer basis points but is not produces an AMOUNT_INVALID finding contained to the one row it was read on, rather than being coerced into the number the engine is meant to audit.
- One term-sheet kernel: the controls and the generator share the re-derivation in sizing_engine.terms, so a determination cannot disagree with the data that produced it.
- Every expiry test is measured against the as_of pricing date carried in the file, never the system clock, so the same file returns the same verdict on any day.
- Deterministic and byte-stable: same inputs produce the same findings in the same order, with no timestamps or absolute paths in any output.
- Absent evidence is never a passing control; a missing artifact fails completeness rather than letting the controls that read it pass unread.
Operating limits
what it refuses to do- The engine never sizes a loan, negotiates a term, advances a draw, moves a dollar, or writes to a source artifact. It reads the sizing file and stops.14
- It reads the seven artifacts the term-sheet workbook emits. It does not connect to a lender, a rate provider, a loan servicing system or a general ledger, and whether a draw is actually funded against a budget line is a separate engine's scope.15
- It proves the term sheet's arithmetic reconciles to the terms the term sheet states -- not that those terms are commercially appropriate, that credit approved them, or that the deal should close. That judgment is a credit officer's and a reviewer's.16
- It takes the index, spread, floor, cost basis and fee rates on file at face value and re-derives from them; it does not source or verify a reference rate, and every index name in the corpus is invented rather than a published benchmark.17
- It confirms the register, the schedules and the rollup foot and tie, not that a deal, a project or a cost basis exists. All shipped data is fictional and the workbook period is set in a fictional future.18
See it run
control architecture
Integration
how to run itDistribution: public repository, MIT license.
- 1python -m pytest --collect-only -q -o addopts= (debt-sizing-automation/)
- 2len(sizing_engine.engine.REGISTRY)
- 3distinct rule-id prefixes in sizing_engine.engine.REGISTRY
- 4samples/*.json after python run.py
- 5len(sizing_engine.model.DOC_TYPES)
- 6sizing_engine/tests boundary tests on the cent, the basis point, the month and the expiry date
- 7python run.py (debt-sizing-automation/); sizing_engine/tests planted-defect suite
- 8sizing_engine/generate.py DEALS DL-203 and DEFECTS loan_over_advance -> size_ltc_not_exceeded
- 9sizing_engine/generate.py BUILDUPS DL-202 and DEFECTS all_in_wrong -> rate_all_in_recomputes
- 10python -m pytest --collect-only -q -o addopts= (debt-sizing-automation/)
- 11len(sizing_engine.engine.REGISTRY)
- 12len(sizing_engine.generate.DEFECTS)
- 13sizing_engine/tests: every registered rule has a planted defect
- 14debt-sizing-automation/README.md and sizing_engine read-only / determinism tests
- 15sizing_engine/model.py DOC_TYPES and the scope note in sizing_engine/engine.py
- 16sizing_engine/engine.py REGISTRY
- 17sizing_engine/terms.py and sizing_engine/model.py INDEX_NAMES
- 18sizing_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