ENGINE 36

SFS-E36-DEP · ASSET REGISTER · REV 2026-07-24 · PRODUCTION

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

Depreciation & Prepaid Amortization

Does every derived figure re-derive from cost, life and window, and do the totals tie?

An entity closes a month with a fixed-asset register and a prepaid schedule full of tiny derived numbers -- a monthly charge per asset, a months-used count, an accumulated balance, a net book value, a per-item amortization waterfall -- and every one of them was once a formula somebody could have overtyped. This engine proves, row by row, that each stated figure was recomputed from the base facts rather than typed, and that the register totals tie the accounts and the entry that post them. The monthly charge is cost over life in integer cents; accumulated depreciation is that base times months-used, capped at cost; net book value is cost less accumulated; every prepaid figure is the additions spread across the stated amortization window. This engine reads the eight artifacts a month-end fixed-asset and prepaid close emits -- the fixed-asset register, the useful-life class table, the capitalization policy and its expense log, the prepaid register, the disposal register, the accumulated-depreciation and prepaid roll-forwards, the GL control balances and the posted monthly recurring journal entry -- and runs thirty deterministic controls over them. Is the close file complete and the close month readable, does every expense item sit on the correct side of the capitalization threshold and every capitalized asset meet it, is every asset's class in the life table and its life the class's (or its lease term) and everything straight-line, does the monthly charge re-derive as cost over life and the close-month expense match the schedule, is the end-of-depreciation month in-service plus life with everything past it fully accumulated at zero net book value and nothing depreciating before service, is months-used capped at the life and the accumulated balance and net book value re-derived, do the accumulated and prepaid roll-forwards close on themselves and foot the registers they summarise, does every prepaid waterfall figure re-derive from its window with the first booked month taking the catch-up, is every disposed asset off the register and each write-off exactly the net book value removed, and do the cost, accumulated-depreciation and prepaid control accounts and the posted recurring journal entry each tie the register footings. It compares every figure with exact ==, gates the threshold with exact >=, measures every date to the close month the file carries rather than a clock, and never writes to a source artifact.

A depreciation register is the safest-looking schedule in the ledger: a column of small charges, each a fraction of a cost, footing to a total that has tied for years. That is exactly why it drifts. None of the ways it goes wrong look wrong in a register that still foots. A useful life is keyed off the class table, a monthly charge is overtyped, an asset keeps depreciating a month past its end date or starts a month before it enters service, and every downstream month inherits the error while the column still adds up. Accumulated depreciation is a running balance -- opening plus the month's charge less what a disposal removed -- and a balance maintained by hand drifts the first time a row is edited and nobody re-adds the column; prepaid balances break the same way. And the register total quietly stops tying: the register says one number, the GL control account says another, and the recurring entry posts a third, each plausible alone. The engine rebuilds every derived figure from cost, life, convention and the stated amortization window through one schedule kernel and compares with exact ==, closes both roll-forwards and foots them to their registers, ties the register totals to the GL control accounts and the posted recurring entry, ships a planted-defect file for every registered control, and states its benchmark as a command rather than a claim. A life keyed wrong, a balance re-typed, or a total that stopped tying is invisible until someone re-derives the register the books already posted.

Run it Book a consultation

Architecture

functional block stack · static overview
Structural Precondition Capitalization Gate Useful-Life Class Mapping Straight-Line Re-derivation In-Service & End-Date Gates Net Book Value Balance Roll-Forwards Prepaid Amortization Waterfall Disposal Roll-Off GL Control-Account Tie-Out Recurring-JE Tie-Out substrate: seeded fictional data

Select a layer

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

Key specifications

at a glance
10,4401
Tests
the collected checks that pin this engine's behavior
302
Controls
independent rules run over every close file
113
Families
groups of controls, each answering one question
324
Close files
one clean baseline plus thirty-one planted-defect files
85
Artifacts read
asset register, life table, capitalization policy, prepaid register, disposal register, roll-forward, GL balances, recurring entry
06
Tolerance
a figure a cent off the re-derivation is a break, not a rounding band

What it does for you

plain terms

An entity closes a month with a fixed-asset register and a prepaid schedule full of tiny derived numbers -- a monthly charge per asset, a months-used count, an accumulated balance, a net book value, a per-item amortization waterfall -- and every one of them was once a formula somebody could have overtyped. Three failures hide inside that, and none of them look wrong in a register that still foots. The derivation drifts: a useful life is keyed off the class table, a monthly charge is overtyped, an asset keeps depreciating past its end date or starts before it enters service, and every downstream month inherits the error while the column still adds up. The roll-forward breaks: accumulated depreciation is a running balance -- opening plus the month's charge less what a disposal removed equals closing -- and a balance maintained by hand drifts the first time a row is edited and nobody re-adds the column, and prepaid balances break the same way. The totals stop tying: the register says one number, the GL control account says another, and the recurring entry posts a third, each plausible alone. None of these are judgment calls. They are equalities -- a monthly charge against cost over life with exact ==, a life against the class table, an accumulated balance against its re-derivation, a control-account balance against the register footing -- which a deterministic control settles better than a spreadsheet inherited by whoever maintains the register this year.

The seeded demo runs every control over every close file7. The CLI generates thirty-two fictional close files -- one clean baseline and one carrying each of the thirty-one planted defects -- then runs all thirty 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 lifecycle gates are genuinely independent8. The date and net-book-value controls partition the assets by stage, so a break at one boundary trips exactly one control. One defect file leaves net book value on an asset that finished its schedule and trips date_fully_depreciated_zero; another books accumulated depreciation on an asset not yet in service and trips date_not_in_service_yet -- each row re-derived so the cost-less-accumulated identity still holds, and the in-life accumulated control skips both, proving the gates own different failures.

The roll-forward is recomputed, not read back9. A defect file shifts the accumulated roll-forward's opening and closing together, so it still closes on itself while drifting off the register it summarises. roll_closing_ties_register rebuilds the footing from the asset register's stated columns and fails, proving the roll-forward is recomputed from the register beneath it rather than trusted because its own identity holds.

Functional block diagram

engineering · each block links to its source
Functional block diagram of the Depreciation & Prepaid Amortization engine close files complete complete complete complete complete complete complete complete complete complete resolves resolves resolves resolves resolves resolves resolves resolves resolves resolves register total findings findings findings findings findings findings Seeded Close Files StructuralPrecondition Capitalization Gate Useful-LifeClass Mapping Straight-LineRe-derivation In-Service &End-Date Gates Net Book Value Balance Roll-Forwards Prepaid AmortizationWaterfall Disposal Roll-Off GL Control-AccountTie-Out Recurring-JE Tie-Out Registers,Table & Kernel Verdict and Findings SFS-E36-DEP · FUNCTIONAL BLOCK DIAGRAM · REV 2026-07-24

Plain terms

  • Seeded Close Files. fictional month-end close files enter the control registry
  • Structural Precondition. is the close file complete and the close month legible
  • Capitalization Gate. is every item on the correct side of the threshold
  • Useful-Life Class Mapping. is every life and method the class table's
  • Straight-Line Re-derivation. does every charge re-derive from cost and life
  • In-Service & End-Date Gates. do the service and end-date gates hold
  • Net Book Value. does the accumulated balance and NBV re-derive
  • Balance Roll-Forwards. do the roll-forwards close and foot the registers
  • Prepaid Amortization Waterfall. does every prepaid waterfall figure re-derive
  • Disposal Roll-Off. are disposals rolled off, written off, and watched
  • GL Control-Account Tie-Out. do the control accounts foot the registers
  • Recurring-JE Tie-Out. does the posted entry equal the register totals
  • Registers, Table & Kernel. the artifacts and shared kernel every rule resolves against
  • Verdict and Findings. every finding, with the reason it exists, ends at a person

Engineering

  • Seeded Close Files. generate_corpus writes one clean baseline plus one planted-defect file for every registered control, thirty-two files in total. Only the base facts are stated -- each asset's cost, class, method, in-service month and life, each prepaid item's additions and amortization window, the policy threshold and the disposal facts -- and every derived column the engine later re-checks (the monthly charge, months used, accumulated depreciation, the close-month expense, net book value, the end-of-depreciation month, the prepaid waterfall figures, both roll-forwards, the GL control balances and the recurring entry) is computed through the same schedule 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 eight artifact types or carrying a duplicate, or an unreadable close month, so no downstream rule holds vacuously on absent evidence.
  • Capitalization Gate. Two controls: every expense-log item sits on the correct side of the capitalization threshold in both directions, and no capitalized asset on the register falls below it, gated with exact >=.
  • Useful-Life Class Mapping. Three controls: every asset's class is in the life table, its life is the class's fixed life or its own lease term, and every asset and class row states the straight-line method.
  • Straight-Line Re-derivation. Two controls: the monthly charge is cost over life in integer cents, and the close-month expense is the schedule's charge, with the final month absorbing the remainder so the schedule sums to cost exactly.
  • In-Service & End-Date Gates. Three controls that partition assets by lifecycle stage: the end month is in-service plus life, an asset past it is fully accumulated at zero net book value, and an asset before service carries no depreciation.
  • Net Book Value. Three controls: months-used is elapsed capped at the life, the in-life accumulated balance re-derives as base times months-used capped at cost, and net book value equals cost less accumulated inside [0, cost].
  • Balance Roll-Forwards. Three controls: the accumulated-depreciation roll-forward closes on itself, its closing and charge foot the asset register, and the prepaid roll-forward closes and foots the prepaid register.
  • Prepaid Amortization Waterfall. Five controls: the window is usable, the monthly charge and the close-month expense (catch-up included) and both balances re-derive from the window, and a window about to end is flagged.
  • Disposal Roll-Off. Three controls: no disposed asset is still on the register, each write-off is exactly the net book value removed, and an asset stale past its end month is flagged for disposal review.
  • GL Control-Account Tie-Out. Three controls: the cost, accumulated-depreciation and prepaid control accounts each equal the register footing they summarise, compared with exact ==.
  • Recurring-JE Tie-Out. Two controls: the posted depreciation line equals the register's total monthly charge, and the posted amortization line equals the prepaid register's monthly total, each posted from the schedule.
  • Registers, Table & Kernel. The fixed-asset register carries each asset's cost, class, method, in-service month, life and its derived charge, months-used, accumulated, end month and net book value; the useful-life table carries the life and method per class; the capitalization policy carries the threshold, the excluded categories and the expense log; the prepaid register carries the amortization waterfall; the disposal register carries the write-offs; the roll-forward schedule carries both balance identities; and the GL control balances and the recurring entry carry the amounts the registers must tie to. Every derivation, date, roll-up and tie-out control resolves against these through the shared schedule kernel, so a stated figure cannot disagree with the arithmetic that produced it.
  • Verdict and Findings. Findings roll up per close 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 thirty 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 designdep_report.json, dep_report.md, samples/
python -m depreciation_engine samples
analyze an existing folder of close files without regenerating itper-file verdicts and findings0 PASS / 1 REVIEW / 2 FAIL / 3 usagenone unless --json or --md is passed
python -m depreciation_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 depreciation_engine/tests -q
run the full test suite for this engine10440 passed0 when every test passesnone

Benchmarks

measured demo results
MeasureResult
Engine tests1010,440 tests collected
live collection from the engine directory
Registered controls1130 controls
counted from the registry, not from documentation
Planted defects1231 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 figure is carried into the close and whether an asset is chased for disposal or a prepaid term for renewal.

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 close file into the documented review and sign-off
One or more FLAG, no FAILREVIEWa human resolves each flag; an asset sitting fully depreciated past the disposal-review band and a prepaid window about to end both land here
One or more FAILFAILthe figure is not carried until the failing control is cleared -- a charge that does not re-derive, a life off the class table, a roll-forward that does not close, or a total that does not tie the GL or the recurring entry

Operating limits

what it refuses to do

See it run

control architecture
Corporate roll-forward architecture for Depreciation & Prepaid Amortization: opening state and period activity pass through deterministic controls to ending balances and an independent reporting tie-out.
The Depreciation & Prepaid Amortization engine tile states the question that has to hold row by row: every monthly charge, accumulated balance, net book value and prepaid waterfall figure recomputed from cost, life, convention and the stated window, both roll-forwards closed and footed to their registers, and the register totals tied to the GL control accounts and the posted recurring entry.

Integration

how to run it

Distribution: public repository, MIT license.

python run.py
python -m pytest depreciation_engine/tests -q
Source on GitHub Engine README All tests, by engine Run in Codespaces
Substantiation
  • 1python -m pytest --collect-only -q -o addopts= (depreciation-register-automation/)
  • 2len(depreciation_engine.engine.REGISTRY)
  • 3distinct rule-id prefixes in depreciation_engine.engine.REGISTRY
  • 4samples/*.json after python run.py
  • 5len(depreciation_engine.model.DOC_TYPES)
  • 6depreciation_engine/tests boundary tests on the cent, the month and the threshold
  • 7python run.py (depreciation-register-automation/); depreciation_engine/tests planted-defect suite
  • 8depreciation_engine/generate.py DEFECTS fully_depreciated_still_carrying / depreciation_before_service
  • 9depreciation_engine/generate.py DEFECTS rollforward_untied_to_register -> roll_closing_ties_register
  • 10python -m pytest --collect-only -q -o addopts= (depreciation-register-automation/)
  • 11len(depreciation_engine.engine.REGISTRY)
  • 12len(depreciation_engine.generate.DEFECTS)
  • 13depreciation_engine/tests: every registered rule has a planted defect
  • 14depreciation-register-automation/README.md and depreciation_engine read-only / determinism tests
  • 15depreciation_engine/model.py DOC_TYPES
  • 16depreciation_engine/engine.py and model.py scope notes
  • 17depreciation_engine/engine.py life_ and cap_ families
  • 18depreciation_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