Skip to content
Code Recycle

Component · for humans & their agents

Rent-Roll Diff

verified · first-partyactively maintained$0 during beta (was $49)

A rent roll tells you who is in the building today. It cannot tell you who renewed — and asking a model to eyeball two of them produces a confident answer with no way to check it.

by parsley · Code Recycle moderator

Get it free — beta

Every claim on this page is refundable if it is untrue — refund policy.

Verified: 28 tests · 10/10 mutations caught

Renewal is a claim about two points in time. A single roster cannot answer it, so the only honest method is to compare dated snapshots and classify what moved.

Why this is not a prompt

Renewal is a claim about two points in time. A single roster cannot answer it, so the only honest method is to compare dated snapshots and classify what moved.

Hand that to a model and it reads two rosters, sees the same tenant twice, and says "renewed" — fluently, without noticing that the expiration moved EARLIER, that the suite label changed building format, or that "EKN 200 dba Trimana" and "Trimana" are one tenant while "Pacific Dental Group" and "Pacific Imaging Group" are two. The classification belongs in code. The model orchestrates, presents, and cites.

What it decides

| event | how it is observed | |---|---| | `renewal` | same tenant, same premises, later expiration than the prior snapshot | | `new_lease` | present in the later roll, absent from the earlier | | `departure` | present in the earlier roll, absent from the later | | `relocation` | same tenant, different premises | | `expansion` / `contraction` | premises added or dropped | | `holdover_suspect` | still on the roll past expiration with no new term | | `data_correction_suspect` | expiration moved earlier, or sqft shrank on the same term | | `unchanged` | matched, nothing moved |

The four things that make this harder than it looks

Renewal ≠ expiration ≠ unexercised option. An expiration that moves earlier is not a renewal — it is an amendment, an early termination, or a data-entry error, and it is surfaced as a suspect rather than silently reclassified.

"Renewed in 2026" is ambiguous. Signed-in-year and term-commencing-in-year are different questions, and rent rolls carry no signed date. Events expose both the observation window and the new term's commencement, and `renewalsDuring()` makes the caller state which one they mean.

Tenant names vary across documents. Matching is tiered — exact, token-set, alias, partial — each with its own confidence, so a weak match arrives labelled weak. Cross-vocabulary equivalences (Wabtec = Westinghouse Air Brake Technologies) come only from an alias table you supply; they are never guessed.

Month-to-month conversions never appear on a leases-signed report. A tenant sitting past expiration with no new term is the one place they surface, so it has its own event type.

What it does not do

It does not read PDFs — you supply rows. It does not decide that a partial name match is really the same tenant — it tells you the match was partial and flags the event. It carries no rent or escalation math: it classifies lease events, not economics.

Verified

28 tests, and 10 deliberate defects applied to the real source — all 10 caught. Two of them are the ones that matter commercially: an expiration moving earlier classified as a renewal, and a tenant matched on nothing but a shared industry word. The mutation set ships in the source so you can re-run it against your own edits.

Delivery

Source delivered as a private repository invite within 24 hours of purchase. Single-product commercial license: use and modify in any number of products; no redistribution or resale of the source.

Interface

What you call, and what comes back. Types and signatures only — the implementation ships with the source.

  export function tenantTokens(name: string): Set<string>;
  export function tenantNamesMatch(a: string, b: string, aliases?: string[][]): NameMatchTier;
  export function parseSuite(raw: string): SuiteKey;
  export function buildingsCompatible(a: string | null, b: string | null): boolean;
  export function suitesMatch(a: string, b: string):;
  export function diffRentRolls(prev: RentRollSnapshot, next: RentRollSnapshot, opts: DiffOptions = {}): LeaseChangeEvent[];
  export function diffRentRollSeries(snapshots: RentRollSnapshot[], opts: DiffOptions = {}): LeaseChangeEvent[];
  export function renewalsDuring( events: LeaseChangeEvent[], year: number, definition: RenewalDefinition, ):;
  export type SuiteKey = { building: string | null;

01Capabilities

Does

  • + Property portfolio management
  • + Rent roll & CAM reporting
  • + Structured document extraction
  • + Entity resolution

Doesn’t

  • No exclusions declared

02Requirements & stack

Depends on

No declared dependencies

Credentials needed

None declared

Stack

typescript

03Community

No endorsements yet

No verified confirmations yet — be the first.

Confirmations come from verified purchasers, installers, vetted reviewers, or an installation outcome your org reported through the agent tools. They grade quality — security is verified separately, and community votes can never override the security gate.

Open an issue

Sign in to confirm — weight comes from verified usage, not vote count.

0 open · 0 answered · 0 fixed · 1 said it worked

04Trust Passport

Full passport →
–/100

0/0 automated components pass. An automated score is never a security guarantee.

✓ Verified · first-partyreviewed Sep 20, 2026 · re-verification due Dec 19, 2026
  • publisher identity Publisher status verified; 1 verification(s) on file
  • malicious pattern scan No known malicious-behavior patterns across 8 source file(s) plus listing text
  • capability contract All 0 observed capability reference(s) match the declared manifest
  • agent safety scan No injection patterns in agent-readable content
  • provenance No release signature or provenance attestation
  • behavioral sandbox Not performed in this environment — requires the production isolated runner (docs/sandbox-requirements.md). No untrusted code is ever executed on the application host.

Every listing must pass this review before it can be sold, and it is re-run on every release. Verification describes what we checked — it is not a guarantee that the software is safe.

VersionChannelReleasedNotes
1.0.0stableAug 10, 2026First public release.