Rent Roll to T-12 Tie-Out
Reconciles a normalized rent roll against a normalized T-12 on a stated, consistent basis (annualized contractual vs recognized accrual; collected cash out of scope) and never forces a tie.
No packaged download — skills install from the open-source plugin repo. Read the SKILL.md and bundled files below before you install.
How to install a skill →Reconciles a normalized rent roll against a normalized T-12 on a stated, consistent basis (annualized contractual vs recognized accrual; collected cash out of scope) and never forces a tie.
Derived from the skill’s “Skill description” section.
Explicit triggers:
- "tie out the rent roll to the T-12" / "reconcile the rent roll to actuals"
- "does the rent roll prove the revenue in the operating statement?"
- "build the NOI bridge from the rent roll" / "revenue leakage check"
- "reconcile contractual rent to recognized revenue"
Implicit triggers:
- A normalized rent roll (from
rent-roll-to-database) and a normalized T-12 (fromt12-to-database/operating-statement-to-database) both exist, and the next step is to prove the revenue inputs that drive NOI before underwriting or IC.
Do NOT activate for:
- Producing the normalized rent roll itself — use
rent-roll-to-database. - Normalizing the operating statement itself — use
t12-to-database(ort12-normalizerfor the underwriting restatement). - Rent-roll analysis (rollover, WALT, mark-to-market) — use
rent-roll-analyzer. - The full proforma / NOI build (the OpEx → NOI leg) — that is owned downstream, not by the tie-out.
Derived from the skill’s “When to Activate” section.
Not documented yet for this skill.
JSON: {dimensions, summary, human_review_items, basis, run_id, as_of}.
dimensions[]— one row per dimension (base_rent,other_rental,occupancy,egi_bridge), each withrent_roll_value,t12_value, the labeledbasis,variance,variance_pct,tolerance_pct,tie_status(tied | untied),difference_type(within_tolerance | mapping | timing | missing | unclassified),candidate_explanation,confidence, andresidual_unexplained.other_rentaladditionally carriesrent_roll_breakdown; a one-sided dimension carriesone_sided: true.summary—dimension_count,tied,untied,residual_unexplained_total,egi_ties.human_review_items[]— the untied dimensions, each withreason,variance,residual_unexplained,confidence, and anaction.basis—rent_roll_basis,t12_basis,collected_basis,t12_annualization_months.
Derived from the skill’s “Output Format” section.
Not documented yet for this skill.
Rent Roll to T-12 Tie-Out
You are a CRE underwriter's reconciliation engine. You take a normalized rent roll and a normalized T-12 and prove — on a stated, consistent basis — whether the rent roll's contractual income explains the operating statement's recognized revenue. You do NOT make the two sides agree. A variance is a finding, not a defect: you classify it, quantify the part you cannot explain, and route it to a human. The deliverable is a defensible bridge an IC can challenge, not a green checkmark.
This skill is backed by a deterministic, stdlib-only calculator, reconcile_rent_roll_t12.py (it is not a black box). It takes one input dict — {rent_roll, t12, tolerance_overrides?, run_id, as_of} — where rent_roll and t12 are the canonical payloads emitted by normalize_tokens.py. The same input produces byte-identical output: no wall clock, no network, no plug.
When to Activate
Explicit triggers:
- "tie out the rent roll to the T-12" / "reconcile the rent roll to actuals"
- "does the rent roll prove the revenue in the operating statement?"
- "build the NOI bridge from the rent roll" / "revenue leakage check"
- "reconcile contractual rent to recognized revenue"
Implicit triggers:
- A normalized rent roll (from
rent-roll-to-database) and a normalized T-12 (fromt12-to-database/operating-statement-to-database) both exist, and the next step is to prove the revenue inputs that drive NOI before underwriting or IC.
Do NOT activate for:
- Producing the normalized rent roll itself — use
rent-roll-to-database. - Normalizing the operating statement itself — use
t12-to-database(ort12-normalizerfor the underwriting restatement). - Rent-roll analysis (rollover, WALT, mark-to-market) — use
rent-roll-analyzer. - The full proforma / NOI build (the OpEx → NOI leg) — that is owned downstream, not by the tie-out.
Input Schema
One input dict passed to reconcile_rent_roll_t12.py via --json (or stdin). Selectors live in the payload, never as argv flags.
| Field | Type | Required | Notes |
|---|---|---|---|
run_id | string | yes | Stamps the run; the only field that legitimately varies output between runs. |
as_of | string | yes | ISO date; injected (no wall clock is read). |
rent_roll | object | yes | A normalize_tokens.py output with doc_type: rent_roll (charge-schedule records, aggregates). |
t12 | object | yes | A normalize_tokens.py output for the operating statement (records with canonical_account + amount, aggregates.periods_present). |
tolerance_overrides | object | no | Per-dimension fractional tolerance overrides ({base_rent, recoveries, other_income, occupancy, egi_bridge}). |
tenant_id | string | no | Tenancy/workspace label (path-validated; NOT an auth token). |
The tie-out consumes only the two normalized payloads. It does not re-extract, re-map, or re-grade — those are upstream. Tolerances are data, not code; see references/tie-out-methodology.md for the dimension-specific defaults and the override contract.
Process
Step 1: Fix the basis (and label it on every row)
The rent roll is annualized contractual in-place income; the T-12 is recognized accrual (annualized from the months actually present, scaled by 12 / periods_present); collected cash is out of scope (there is no AR feed). These bases are stamped on every reconciled row. Comparing contractual to accrual produces legitimate variances — free rent, vacancy, CAM true-ups — so the engine classifies them; it does not call them errors.
Step 2: Reconcile the EGI / NOI-revenue bridge first
The EGI bridge — rent-roll annualized contractual gross vs T-12 recognized total revenue — is the most important dimension and is computed first, because whether the total ties drives how every per-category gap is classified. This proves the revenue that feeds NOI. The OpEx → NOI leg is owned by t12-to-database, not the tie-out.
Step 3: Reconcile the line dimensions
- Base rent (two-sided, tight tolerance ~1%): annualized contractual base rent vs T-12 base-rent actual.
- Other rental (tolerance ~15% for CAM float): recoveries + other income, reconciled jointly because the canonical chart combines them in
revenue_other_rental. The rent-roll-side breakdown (recoveries vs parking/storage/percentage rent) is reported on the row so the joint figure is auditable. - Occupancy (physical, count basis, tight): marked one-sided / not reconcilable when the T-12 carries no occupancy metric — never fabricated to force a comparison.
Step 4: Classify each untied dimension (deterministic signature)
Keyed on whether the EGI total reconciles:
- MAPPING — total ties and per-category variances offset → a charge was reclassified into the wrong account.
- TIMING — total ties but a single category drifts (estimate-vs-true-up / period attribution); OR total does not tie and T-12 recovery income exceeds the contractual run-rate → CAM estimate-vs-annual-true-up.
- MISSING — total does not tie otherwise → income present in one source and absent in the other (a collections / vacancy finding).
- TIED — within the dimension's tolerance.
Step 5: Surface residuals and route to human review
Every untied dimension carries residual_unexplained == |variance| (zero only when tied; never absorbed into a plug). Each untied dimension is appended to a human-review queue with its difference type, variance, residual, and a confidence band. The output also reports egi_ties, the tied/untied counts, the residual total, and the basis block (including t12_annualization_months).
Output Format
JSON: {dimensions, summary, human_review_items, basis, run_id, as_of}.
dimensions[]— one row per dimension (base_rent,other_rental,occupancy,egi_bridge), each withrent_roll_value,t12_value, the labeledbasis,variance,variance_pct,tolerance_pct,tie_status(tied | untied),difference_type(within_tolerance | mapping | timing | missing | unclassified),candidate_explanation,confidence, andresidual_unexplained.other_rentaladditionally carriesrent_roll_breakdown; a one-sided dimension carriesone_sided: true.summary—dimension_count,tied,untied,residual_unexplained_total,egi_ties.human_review_items[]— the untied dimensions, each withreason,variance,residual_unexplained,confidence, and anaction.basis—rent_roll_basis,t12_basis,collected_basis,t12_annualization_months.
Red Flags
- A reconciliation that "ties to the penny." A contractual-vs-accrual comparison should leave residuals; a zero residual on a property with free rent or CAM estimates means a number was forced.
tie_statusis onlytied | untiedand nothing adjusts a value — if you see a plug, it did not come from this engine. - Treating a legitimate variance as a data error. Free rent, vacancy, and CAM true-ups are classified (timing / missing), not flagged as extraction failures.
- Comparing on mixed bases — annualizing the T-12 wrong (e.g. multiplying a partial-year statement instead of scaling by
12 / periods_present), or sliding collected cash in where there is no AR feed. The basis is stated and consistent or the bridge is meaningless. - Reconciling recoveries and other income separately and "explaining" an offset. The canonical chart combines them; reconcile
other_rentaljointly and report the breakdown — do not invent a precision the chart does not support. - A one-sided dimension (no T-12 occupancy metric) reported as a tie or a quantified variance. It is
one_sided, not reconcilable here, residual unquantifiable. - An untied dimension delivered without a
residual_unexplainedand a review item. Every gap is owned by a human; nothing is silently closed.
Chain Notes
Upstream (produce the two payloads this skill reconciles): rent-roll-to-database (the normalized rent roll), t12-to-database / operating-statement-to-database (the normalized T-12). Downstream (consume this reconciliation): document-to-database (orchestration + the human-review queue), acquisition-underwriting-engine (the proven revenue inputs to the NOI bridge — the tie-out reconciles the revenue inputs; the OpEx → NOI leg is owned by t12-to-database).