Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ jobs:
- name: Counterexamples and contracts
run: python -m unittest discover -s tests -v
- name: Replay committed evidence
run: pitbridge verify --out demo
run: |
pitbridge verify --out demo
pitbridge verify-rolling --out demo/rolling
- name: Generate and replay a fresh report
run: |
pitbridge demo --out outputs
pitbridge verify --out outputs
pitbridge rolling-demo --out outputs/rolling
pitbridge verify-rolling --out outputs/rolling
4 changes: 3 additions & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ jobs:
run: |
python -m pip install -e .
pitbridge verify --out demo
pitbridge verify-rolling --out demo/rolling
- name: Prepare the standalone report
run: |
mkdir _site
cp demo/* _site/
cp -R demo/. _site/
cp demo/report.html _site/index.html
cp demo/rolling/report.html _site/rolling/index.html
touch _site/.nojekyll
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v4
Expand Down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,25 @@ The production path is a SQLite window-function join. A separate Python enumerat

Verification checks hashes **and** reruns both algorithms, rejecting a fabricated summary even if its hash was updated. Hashes are not signatures and cannot establish that an external source is truthful.

## Availability-aware rolling features

Rolling sums, observation means and counts resolve availability, revisions
and tombstones before aggregating an inclusive event-time window. Every result
retains all contributing records; a separate Python temporal enumerator checks
membership and values.

```bash
pitbridge rolling-demo --out outputs/rolling
pitbridge verify-rolling --out outputs/rolling
pitbridge aggregate --inputs demo/rolling/inputs.json --out outputs/custom
```

[Online rolling case](https://dev-belly.github.io/PITBridge/rolling/) ·
[Contract and worked example](docs/ROLLING.md) ·
[Feature rows](demo/rolling/features.csv) · [Membership](demo/rolling/members.csv)

## Scope

This is a reference implementation for scalar financial observations. It does not provide streaming ingestion, access control, label generation or aggregated rolling features. Version numbers and trustworthy availability timestamps must come from the upstream source contract. SQLite is intentionally inspectable; distributed-scale performance has not been benchmarked.
This is a reference implementation for scalar financial observations and explicit rolling aggregates. It does not provide streaming ingestion, access control or label generation. Version numbers and trustworthy availability timestamps must come from the upstream source contract. SQLite is intentionally inspectable; distributed-scale performance has not been benchmarked. Rolling means are observation means, and counts do not establish complete business activity.

PITBridge complements [CreditVintage](https://github.com/dev-belly/CreditVintage)'s application-time model evaluation. The repositories are separate components; no integration is claimed. MIT license.
8 changes: 8 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ python -m unittest discover -s tests -v
- **显式缺失:** 区分没有历史、尚未可用、已撤销和已过期,保留申请行。
- **逐条溯源:** 导出来源、记录 ID、版本、发生时间、公开时间与入库时间。
- **可重放证据:** 修改报告并更新哈希,仍会被语义重放发现。
- **时点滚动统计:** 在选择当时已知版本后,计算指定窗口的加总、观察均值和计数,并保留每条贡献记录;SQL 与 Python 分别重放成员与数值。

[在线滚动特征案例](https://dev-belly.github.io/PITBridge/rolling/) · [窗口合同与手算案例](docs/ROLLING.md)

```bash
pitbridge rolling-demo --out outputs/rolling
pitbridge verify-rolling --out outputs/rolling
```

合成示例有 11 条源记录、15 次决策、45 次特征查询。错误的“只按发生时间关联”基线有 11 次使用未来信息;完整规则共改变 16 次选择。后者还包括撤销和过期影响,不能把两个数字混为一谈。

Expand Down
2 changes: 1 addition & 1 deletion demo/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": {
"comparison.csv": "0eb458eceec7477682417da3bcfc24c1746021c52ad83b24c7a4a7e5fc4f734e",
"inputs.json": "d55c44483897bd3ed838376b3e7a11f7d56ee41ef5cf8e6e22c99d060d701a98",
"report.html": "39b0dbcc2bd7c13447127990d4cece668fdbc1e15a180c969100dac8fa6d0f4c",
"report.html": "2d09d39b3e4034683a50c1fd566c27100aca20c891ec5b1124ad6713945cdf05",
"snapshots.csv": "777f54dea6c8067cccb9a9e5ec375038a99dd2e44c9ab1df52cfae029a31e6f0",
"summary.json": "fe3d30c39e8eb5244636e150f2da884e898456a1b5f52c2c42f0a65cf54e496b"
},
Expand Down
1 change: 1 addition & 0 deletions demo/report.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*{box-sizing:border-box}body{margin:0;background:#0c1422;color:#e6edf7;font:16px/1.6 system-ui,sans-serif}main{max-width:1200px;margin:auto;padding:48px 24px}h1{font-size:clamp(32px,6vw,64px);line-height:1.1;margin:16px 0}h2{margin-top:44px}p{color:#a9b9cf}.tag{color:#64e5c4;letter-spacing:.14em;font-size:12px}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin:32px 0}.card{border:1px solid #293952;border-radius:14px;padding:22px;background:#111e31}.value{display:block;font-size:38px;font-weight:700}.scroll{overflow-x:auto;border:1px solid #293952;border-radius:12px}table{width:100%;border-collapse:collapse;font-size:13px}th,td{text-align:left;padding:12px;white-space:nowrap;border-bottom:1px solid #22314a}th{background:#17263b;color:#9fefd9}code{color:#9fefd9}a{color:#9fefd9}label{display:block;margin:16px 0;cursor:pointer}footer{margin-top:48px;color:#8ca1bc}.hidden{display:none}
</style><main><div class="tag">PITBRIDGE / SYNTHETIC FINANCIAL DATA</div>
<h1>What was known<br>when the decision was made?</h1>
<p><a href="https://dev-belly.github.io/PITBridge/rolling/">Explore rolling-window features and their source membership</a></p>
<p>Availability-aware SQL snapshots with publication, ingestion, revision and record-level lineage.</p>
<div class="cards"><div class="card"><span class="value">15</span>Decisions</div><div class="card"><span class="value">45</span>Feature lookups</div><div class="card"><span class="value">11</span>Unsafe future-data selections</div><div class="card"><span class="value">16</span>Selections changed</div></div>
<p><b>Independent replay:</b> SQLite output matches the Python enumeration oracle. deleted: 3 · no_history: 17 · not_available: 4 · selected: 16 · stale: 5</p>
Expand Down
13 changes: 13 additions & 0 deletions demo/rolling/features.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
decision_id,entity_id,decision_at,name,source,feature,window_days,aggregation,value,observation_count,status
SME-A-10,SME-A,2026-02-10T18:00:00.000000+00:00,mean_inflow_7d,bank,net_inflow,7.0,mean,150.0,2,selected
SME-A-10,SME-A,2026-02-10T18:00:00.000000+00:00,net_inflow_30d,bank,net_inflow,30.0,sum,1500.0,4,selected
SME-A-10,SME-A,2026-02-10T18:00:00.000000+00:00,observations_30d,bank,net_inflow,30.0,count,4,4,selected
SME-A-11,SME-A,2026-02-11T18:00:00.000000+00:00,mean_inflow_7d,bank,net_inflow,7.0,mean,150.0,2,selected
SME-A-11,SME-A,2026-02-11T18:00:00.000000+00:00,net_inflow_30d,bank,net_inflow,30.0,sum,4300.0,3,selected
SME-A-11,SME-A,2026-02-11T18:00:00.000000+00:00,observations_30d,bank,net_inflow,30.0,count,3,3,selected
SME-A-12,SME-A,2026-02-12T18:00:00.000000+00:00,mean_inflow_7d,bank,net_inflow,7.0,mean,1250.0,2,selected
SME-A-12,SME-A,2026-02-12T18:00:00.000000+00:00,net_inflow_30d,bank,net_inflow,30.0,sum,6300.0,4,selected
SME-A-12,SME-A,2026-02-12T18:00:00.000000+00:00,observations_30d,bank,net_inflow,30.0,count,4,4,selected
SME-B-12,SME-B,2026-02-12T18:00:00.000000+00:00,mean_inflow_7d,bank,net_inflow,7.0,mean,,0,no_history
SME-B-12,SME-B,2026-02-12T18:00:00.000000+00:00,net_inflow_30d,bank,net_inflow,30.0,sum,,0,no_history
SME-B-12,SME-B,2026-02-12T18:00:00.000000+00:00,observations_30d,bank,net_inflow,30.0,count,,0,no_history
170 changes: 170 additions & 0 deletions demo/rolling/inputs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"data_kind": "synthetic",
"decisions": [
{
"decision_at": "2026-02-10T18:00:00.000000+00:00",
"decision_id": "SME-A-10",
"entity_id": "SME-A"
},
{
"decision_at": "2026-02-11T18:00:00.000000+00:00",
"decision_id": "SME-A-11",
"entity_id": "SME-A"
},
{
"decision_at": "2026-02-12T18:00:00.000000+00:00",
"decision_id": "SME-A-12",
"entity_id": "SME-A"
},
{
"decision_at": "2026-02-12T18:00:00.000000+00:00",
"decision_id": "SME-B-12",
"entity_id": "SME-B"
}
],
"observations": [
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-01-11T18:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-01-11T18:00:00.000000+00:00",
"published_at": "2026-01-11T18:00:00.000000+00:00",
"record_id": "boundary",
"revision": 1,
"source": "bank",
"value": 200
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-07T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-07T00:00:00.000000+00:00",
"published_at": "2026-02-07T00:00:00.000000+00:00",
"record_id": "cancel-v1",
"revision": 1,
"source": "bank",
"value": 800
},
{
"deleted": true,
"entity_id": "SME-A",
"event_at": "2026-02-07T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-09T00:00:00.000000+00:00",
"published_at": "2026-02-09T00:00:00.000000+00:00",
"record_id": "cancel-v2",
"revision": 2,
"source": "bank",
"value": null
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-01T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-01T00:00:00.000000+00:00",
"published_at": "2026-02-01T00:00:00.000000+00:00",
"record_id": "cash-v1",
"revision": 1,
"source": "bank",
"value": 1000
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-01T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-11T00:00:00.000000+00:00",
"published_at": "2026-02-11T00:00:00.000000+00:00",
"record_id": "cash-v2",
"revision": 2,
"source": "bank",
"value": 4000
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-14T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-14T00:00:00.000000+00:00",
"published_at": "2026-02-14T00:00:00.000000+00:00",
"record_id": "future-event",
"revision": 1,
"source": "bank",
"value": 300
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-09T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-12T00:00:00.000000+00:00",
"published_at": "2026-02-12T00:00:00.000000+00:00",
"record_id": "late",
"revision": 1,
"source": "bank",
"value": 2000
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-01-11T17:59:59.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-01-11T17:59:59.000000+00:00",
"published_at": "2026-01-11T17:59:59.000000+00:00",
"record_id": "outside",
"revision": 1,
"source": "bank",
"value": 10000
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-08T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-09T00:00:00.000000+00:00",
"published_at": "2026-02-09T00:00:00.000000+00:00",
"record_id": "recent",
"revision": 1,
"source": "bank",
"value": 500
},
{
"deleted": false,
"entity_id": "SME-A",
"event_at": "2026-02-05T00:00:00.000000+00:00",
"feature": "net_inflow",
"ingested_at": "2026-02-05T00:00:00.000000+00:00",
"published_at": "2026-02-05T00:00:00.000000+00:00",
"record_id": "refund",
"revision": 1,
"source": "bank",
"value": -200
}
],
"rolling_specs": [
{
"aggregation": "mean",
"feature": "net_inflow",
"name": "mean_inflow_7d",
"source": "bank",
"window_days": 7
},
{
"aggregation": "sum",
"feature": "net_inflow",
"name": "net_inflow_30d",
"source": "bank",
"window_days": 30
},
{
"aggregation": "count",
"feature": "net_inflow",
"name": "observations_30d",
"source": "bank",
"window_days": 30
}
]
}
11 changes: 11 additions & 0 deletions demo/rolling/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"engine": "pitbridge/rolling/1",
"files": {
"features.csv": "62cbc8dfbd1cfa7567346341aa47b9067039186bd5a6a39d1e0f3f7771ad2695",
"inputs.json": "67480d63496734095c73eca104ddbf3843028be98238637b90facf8ee7859886",
"members.csv": "a5ebb522cc58139169d1655a02d6753a3f49f81f11517656c5152ebfa0251aa1",
"report.html": "8b7aa6e303fa6dbcceae3d630beb42d152673b2cf74aa1eca107099e7912316f",
"summary.json": "a2a15decb3ac74604c8a13dd1f1452695c35bfbab982d7c87ca234ba12fc8b1b"
},
"schema_version": 1
}
29 changes: 29 additions & 0 deletions demo/rolling/members.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
decision_id,name,record_id,revision,event_at,published_at,ingested_at,available_at,value
SME-A-10,mean_inflow_7d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-10,mean_inflow_7d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-10,net_inflow_30d,boundary,1,2026-01-11T18:00:00.000000+00:00,2026-01-11T18:00:00.000000+00:00,2026-01-11T18:00:00.000000+00:00,2026-01-11T18:00:00.000000+00:00,200.0
SME-A-10,net_inflow_30d,cash-v1,1,2026-02-01T00:00:00.000000+00:00,2026-02-01T00:00:00.000000+00:00,2026-02-01T00:00:00.000000+00:00,2026-02-01T00:00:00.000000+00:00,1000.0
SME-A-10,net_inflow_30d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-10,net_inflow_30d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-10,observations_30d,boundary,1,2026-01-11T18:00:00.000000+00:00,2026-01-11T18:00:00.000000+00:00,2026-01-11T18:00:00.000000+00:00,2026-01-11T18:00:00.000000+00:00,200.0
SME-A-10,observations_30d,cash-v1,1,2026-02-01T00:00:00.000000+00:00,2026-02-01T00:00:00.000000+00:00,2026-02-01T00:00:00.000000+00:00,2026-02-01T00:00:00.000000+00:00,1000.0
SME-A-10,observations_30d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-10,observations_30d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-11,mean_inflow_7d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-11,mean_inflow_7d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-11,net_inflow_30d,cash-v2,2,2026-02-01T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,4000.0
SME-A-11,net_inflow_30d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-11,net_inflow_30d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-11,observations_30d,cash-v2,2,2026-02-01T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,4000.0
SME-A-11,observations_30d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-11,observations_30d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-12,mean_inflow_7d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-12,mean_inflow_7d,late,1,2026-02-09T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2000.0
SME-A-12,net_inflow_30d,cash-v2,2,2026-02-01T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,4000.0
SME-A-12,net_inflow_30d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-12,net_inflow_30d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-12,net_inflow_30d,late,1,2026-02-09T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2000.0
SME-A-12,observations_30d,cash-v2,2,2026-02-01T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,2026-02-11T00:00:00.000000+00:00,4000.0
SME-A-12,observations_30d,refund,1,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,2026-02-05T00:00:00.000000+00:00,-200.0
SME-A-12,observations_30d,recent,1,2026-02-08T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,2026-02-09T00:00:00.000000+00:00,500.0
SME-A-12,observations_30d,late,1,2026-02-09T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2026-02-12T00:00:00.000000+00:00,2000.0
Loading
Loading