Skip to content

Compare two loan periods and keep the evidence

Question: How did the scheduled and actual balance fields change between two reporting periods for one CMBS loan record?

Cairn's worked example asks whether this loan paid down in October. This guide shows the comparison behind the narrower answer.

This example uses Data MCP to select two public filing-derived rows, then the local shape, rvl and pack CLIs. Data MCP needs arranged access to the relevant table and role. Request access before trying the query. The handoff from MCP JSON to CSV is manual; these products do not run as one automatic pipeline.

1. Select two filing rows

After finding and describing the table, call edgar_data.run_query with a bounded SELECT. The two accession numbers select the filings compared here. The filing walkthrough shows how edgar.com.entity.filings returned those accessions; save that separate response as filings.json if you repeat the full chain.

{
"sql": "SELECT CIK, ASSETNUMBER, REPORTINGPERIODENDDATE, FILING_DATE, FILING_ID, REPORTPERIODENDSCHEDULEDLOANBALANCEAMOUNT, REPORTPERIODENDACTUALBALANCEAMOUNT FROM EDGAR_DB.PROPERTY_MART.LOAN_PERIOD_FACT WHERE CIK = '1827054' AND ASSETNUMBER = '25' AND FILING_ID IN ('0001888524-26-019266', '0001888524-26-017937') ORDER BY REPORTINGPERIODENDDATE DESC NULLS LAST, FILING_DATE DESC NULLS LAST LIMIT 10",
"limit": 10
}

An authenticated read-only check on November 4, 2026 returned ok: true, row_count: 2, rows_total: 2 and complete: true for this filtered query. Both rows had CIK 1827054 and asset number 25. The older row reported a period ending September 11, 2026, filed September 29 under accession 0001888524-26-017937. The newer row reported a period ending October 13, 2026, filed October 27 under accession 0001888524-26-019266. The filing index supplied September and October SEC archive links; those links were constructed from its index records and were not fetched in this check.

complete: true covers these two selected accessions within the query limit. It does not establish a complete loan history. Keep the full JSON response, including the SQL and row metadata, as data-mcp-source.json.

2. Export one row per period

Manually copy the seven returned fields into september.csv and october.csv, preserving the field names and decimal strings. Check each value against the saved JSON. If either selected balance is missing or nonnumeric, stop; do not export it as zero or infer that the field was unchanged. This export is a visible handoff, not a Data MCP feature.

CIK,ASSETNUMBER,REPORTINGPERIODENDDATE,FILING_DATE,FILING_ID,REPORTPERIODENDSCHEDULEDLOANBALANCEAMOUNT,REPORTPERIODENDACTUALBALANCEAMOUNT
1827054,25,2026-09-11,2026-09-29,0001888524-26-017937,9288290.030000,9399496.500000
CIK,ASSETNUMBER,REPORTINGPERIODENDDATE,FILING_DATE,FILING_ID,REPORTPERIODENDSCHEDULEDLOANBALANCEAMOUNT,REPORTPERIODENDACTUALBALANCEAMOUNT
1827054,25,2026-10-13,2026-10-27,0001888524-26-019266,9268821.880000,9399496.500000

Save this shared scope and key as loan-period-profile.yaml:

include_columns: [CIK, ASSETNUMBER, REPORTINGPERIODENDDATE, FILING_DATE, FILING_ID, REPORTPERIODENDSCHEDULEDLOANBALANCEAMOUNT, REPORTPERIODENDACTUALBALANCEAMOUNT]
key: [CIK, ASSETNUMBER]

The pair is comparable by that key because the query selected the same CIK and asset in two periods. For more rows, check that the full key identifies one row per file; asset number alone is not a cross-deal identity.

3. Check structure, then calculate the change

Run the installed local CLIs against the same files and profile:

shape september.csv october.csv --profile loan-period-profile.yaml --json --explicit --no-witness > shape.json
jq -e '.outcome == "COMPATIBLE" and .checks.row_granularity.rows_old == 1 and .checks.row_granularity.rows_new == 1 and .checks.row_granularity.key_overlap == 1' shape.json
rvl september.csv october.csv --profile loan-period-profile.yaml --exhaustive --audit-fields --explicit --json --no-witness > rvl.json
jq -e '.outcome == "REAL_CHANGE"' rvl.json

Stop if shape reports INCOMPATIBLE or REFUSAL, or if the row and key checks fail. Shape's key overlap is reported but does not itself gate compatibility. Here shape 0.9.0 reported COMPATIBLE, with one row on each side and one matching CIK and asset key.

rvl 0.7.1 reported REAL_CHANGE. Its JSON showed one changed numeric cell: REPORTPERIODENDSCHEDULEDLOANBALANCEAMOUNT fell from $9,288,290.03 to $9,268,821.88, a $19,468.15 decrease. The REPORTPERIODENDACTUALBALANCEAMOUNT field was $9,399,496.50 in both selected rows. The field audit also recorded changes to the reporting-period date, filing date and filing ID. rvl exits with code 1 for REAL_CHANGE; code 2 means refusal or CLI error.

These are two different balance fields. The table describes scheduled balance as stated principal at period end and actual balance as outstanding at the as-of determination date. The comparison does not establish a change in whole-loan balance or explain why the scheduled field moved. Report the field, period and filing accession with each number.

4. Seal the inputs and reports

Keep the source JSON, both CSVs, the profile and both CLI reports together. This run also included a separate filing-index response, filings.json:

pack seal data-mcp-source.json filings.json september.csv october.csv loan-period-profile.yaml shape.json rvl.json --output evidence-pack --no-witness
pack verify evidence-pack --json --no-witness

pack 0.6.0 created sha256:c79f24adb0925009ef66058bb613474e7a7776228df313853e6fde03a4416055. Verification returned outcome: OK with member and pack hashes valid. A new run may have a different pack ID. Pack checks that the retained files have not changed; it does not certify the filing data or the interpretation. For source pointers behind the table, trace its recorded lineage and keep any missing column-level lineage explicit.