尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Covenant: credit early warning platform. Messy SEC filings in, explainable 12-month distress alerts out. Point-in-time XBRL pipeline, entity resolution, ML risk scores.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Credit early-warning platform for lenders: messy SEC filings in, explainable 12-month distress alerts out.
A credit team at a private-credit fund or mid-market lender uploads its borrower list, often a messy spreadsheet of names and maybe tickers. Covenant:
covenant-credit-early-warning-platf.vercel.app: no sign-up, read-only. Visitors get an anonymous session that can read only the demo portfolio (50 real SEC filers, fictional lender).
Hosted on Vercel (Next.js) and Supabase; a GitHub Actions job refreshes SEC data for the demo companies weekly and re-scores them. See docs/deploy.md.
| Area | Result | Evidence |
|---|---|---|
| Ingestion | 4.0M XBRL facts, 337k filings and 5 FRED macro series for a 205-company dev sample; re-runs are idempotent (0 new rows on a second run); SEC rate limit held at ≤ 8 requests/sec | raw.ingest_runs counts |
| Bankruptcy event scan | 25/25 known Chapter 11 filers found in their petition year; 6 suspicious tags read and documented (mis-tags, co-registrants, subsidiary-only filings) | spot-check, review |
| Canonical financials | 25 metrics from 50+ XBRL tags with ordered fallback, year-to-date → quarterly derivation, restatement versions, scale-error quarantine; core metrics complete for 139/179 active companies | coverage report |
| Entity resolution | 100% auto-match precision at 82% coverage on a held-out 100-row messy file (typos, old names, missing/wrong tickers); 0 wrong matches on hand-written subsidiary/namesake traps | evaluation |
| Distress model | LightGBM PR-AUC 0.098 vs 0.019 for an Altman Z'' baseline (5x; base rate 1.1%) on 2022+ test data across ~6,000 companies; 65% of bankruptcies ranked in the riskiest 10%; flagged a median 14 months before filing (151 of 211); beats Altman in every backtest year 2015-2024 | model card |
| Engineering | Leakage test (features recomputed with all later data deleted must not change, and a planted leak must be caught), Postgres row-level security for multi-tenancy, 282 automated tests in CI against a real Supabase database, plus a web build | CI |
flowchart LR
subgraph Sources
SEC[SEC EDGAR<br/>XBRL facts, filings]
FRED[FRED<br/>macro series]
end
subgraph Batch["Python batch jobs"]
ING[ingest<br/>rate-limited clients]
PIPE[pipeline<br/>staging → curated]
ER[entity resolution]
ML[ml<br/>features, models]
end
subgraph Supabase
RAW[(raw Parquet<br/>Storage)]
PG[(Postgres<br/>raw · staging · curated · ml · app<br/>RLS per tenant)]
end
WEB[Analyst console<br/>Next.js]
SEC --> ING
FRED --> ING
ING --> RAW
RAW --> PIPE
PIPE --> PG
ER --> PG
ML --> PG
PG --> WEB
| Phase | Scope | Status |
|---|---|---|
| 0 | Monorepo, config, migrations, row-level security, CI | ✅ Done |
| 1 | SEC + FRED ingestion, dev sample, bankruptcy event scan | ✅ Done |
| 2 | Cleaning & canonical point-in-time financial model | ✅ Done |
| 3 | Entity resolution: upload, matching, review queue | ✅ Done |
| 4 | Features, labels, Altman / logistic / LightGBM models, SHAP, model card | ✅ Done |
| 5 | LLM risk signals from filing text (Claude Haiku), "Ask the filings" Q&A with citations (Claude Sonnet) | ✅ Done |
| 6a | Analyst console + read-only public demo, weekly refresh (live) | ✅ Done |
| 6b | API, borrower upload and review, alerts | Next |
| 7 | Hosting (Supabase + scheduled jobs + Vercel), drift monitoring, alerts | Planned |
Stack: Python 3.12, Polars, PostgreSQL / Supabase (RLS, Storage, pgvector), rapidfuzz, Pandera, scikit-learn, LightGBM with TreeSHAP, Claude API (Phase 5), Next.js (Phase 6). Full build spec: SPEC.md.
packages/common/ tripwire_common: settings (env), YAML config loaders, JSON logging, DB helpers
services/ingest/ SEC + FRED clients, rate limiter, raw Parquet landing, dev sample, Item 1.03 scan
services/pipeline/ raw -> staging -> curated (tag mapping, YTD derivation, versions, events);
entity/ = borrower upload, matching cascade, review queue
services/ml/ labels (text-checked), point-in-time features, models, evaluation, model card
services/ llm, api: one uv workspace member each (empty until their phase)
supabase/migrations/ schema + RLS, applied by the Supabase CLI
supabase/local/ supabase_shim.sql: Supabase roles + auth.uid() for plain-Postgres dev/tests
config/ models.yaml (LLM/ML settings) and domain/finance/ (the finance domain pack)
tests/ common/, db/ (RLS, schema guards), ingest/, pipeline/, entity/
reports/ committed data reports (bankruptcy scan, coverage, entity resolution)
apps/web/ Next.js console (Phase 6)
Requires uv and Docker.
cp .env.example .env # fill in keys as phases need them
make setup # uv sync --all-packages (installs Python 3.12 env)
make db-up # Postgres 17 + pgvector on localhost:54329
make check # ruff, mypy --strict, pytest
make db-reset # optional: wipe the local DB and apply shim + migrations to it
DB tests create and drop their own throwaway database on TEST_DATABASE_URL, so they never
touch your working data. Without a reachable server they skip; with REQUIRE_DB=1 (CI) they fail.
Needs SEC_USER_AGENT (name + email) and FRED_API_KEY in .env, and the local DB (make db-reset).
uv run python -m tripwire_ingest dev # tickers, sample, submissions, companyfacts, fred, scan
uv run python -m tripwire_ingest --help # individual commands
data/ (local storage backend) under content-addressed keys
(.../sha256=<payload hash>/...), indexed in raw.*_index / raw.source_files. Re-running
writes nothing new for unchanged data (counted as unchanged in raw.ingest_runs.counts).max_requests_per_second actually observed.config/domain/finance/dev_sample.yaml is generated once and committed, so
every re-run ingests the same 205 companies.reports/item_103_spotcheck.md (generated) plus
reports/item_103_review.md (manual review). Issues are logged to staging.quarantine.uv run python -m tripwire_pipeline all # staging, curated, events, coverage report
staging.facts; rule violations (non-USD unit, bad
dates, conflicting duplicates, ...) go to staging.quarantine. Output checked by a Pandera contract.curated.financial_metric, 25 canonical metrics from metric_mapping.yaml, with
the chosen tag, every version, and YTD-derived quarters (lineage in derivation).select * from curated.financial_metric_as_of('2024-02-20', '1') returns
each metric as known on that date. Features must read through it.curated.filing_event from 8-K items and NT 10-K / NT 10-Q.reports/metric_coverage.md.tripwire_pipeline.entity turns an uploaded borrower CSV into SEC companies:
name / company / borrower_name …, ticker / symbol …).
Extra columns are ignored.app.match_review with up to 5 candidates. Settings are in config/entity_resolution.yaml.app.entity_alias (RLS), reused on later uploads and
applied to every pending row with the same input. They are never shared across tenants.reports/entity_resolution_eval.md (scripts/eval_entity_resolution.py). On the
100-row hold-out file, 100% auto-match precision with 82% auto-matched. On hand-written
adversarial cases (subsidiaries, namesakes), 0 wrong auto-matches.uv run python -m tripwire_ingest bulk # SEC bulk zips (~3 GB) into raw storage
uv run python -m tripwire_pipeline training-data # 11k companies -> Parquet (same Phase 2 code)
uv run python -m tripwire_ml labels # classify 2.7k Item 1.03 8-Ks from their text
uv run python -m tripwire_ml dataset # quarterly point-in-time features + labels
uv run python -m tripwire_ml train # 3 models, backtest, registry, model card
config/domain/finance/labels.yaml. Estimated precision ≈ 88% from a fresh hand-checked
sample. Rows inside a bankruptcy are excluded.ml.model_version with a promotion rule.uv run python -m tripwire_llm sections # 10-K / 10-Q -> sections; Q&A chunks into Postgres
uv run python -m tripwire_llm eval # evals/text_signals.jsonl on Haiku and Sonnet
uv run python -m tripwire_llm estimate # exact token count on a sample -> cost
uv run python -m tripwire_llm signals --submit # Batch API (half price), refuses over budget
uv run python -m tripwire_llm signals --collect # verify every quote, load curated.text_signal
config/domain/finance/text_signals.yaml). Claude Haiku 5.5 returns
strict JSON; a signal is kept only if its evidence quote is found word for word in the section.
Results are cached by (accession, section, prompt version, model). Loaded: 1,650 signals across
all 50 demo companies from 2,861 section parts; 22 quotes were rejected for not matching the
filing. Total Claude spend for Phase 5 (eval, extraction, Q&A tests): about $3.ml.qa_limits. Citation check.Against a real Supabase stack instead of the shim: supabase db start (database only) or
supabase start (full local stack), then supabase db push to deploy to a linked project.
| Schema | Holds | Who can read |
|---|---|---|
raw | ingest run log, index of raw Parquet files in Storage | service_role |
staging | typed facts and filings, quarantine, LLM call cache | service_role |
curated | canonical point-in-time model: companies, metrics, events, text signals, chunks, macro | service_role |
ml | feature snapshots, labels, model registry, predictions, drift | service_role |
app | tenants, portfolios, matches, alerts, dispositions, audit log | authenticated, own tenant only |
Key decisions:
app table scopes rows to the caller's
memberships (app.user_tenant_ids(), app.has_role()). Children carry tenant_id and
reference parents by the composite (id, tenant_id), so a row cannot point at another
tenant's parent even though FK checks bypass RLS. Column-level UPDATE grants stop users from
moving rows across tenants or editing fields like an alert's reason.curated.financial_metric, ml.prediction, app.disposition,
app.audit_log and the raw indexes reject UPDATE/DELETE with a trigger. Restatements are new
versions; derived values (e.g. Q4 = FY − 9M) must carry derivation lineage.filed_date. ml.feature_snapshot
has a check that its max_source_filed_date <= as_of_date, a last-line leakage guard behind
the Phase 4 leakage test. Macro data is bitemporal (observation_date, release_date).util.cik (10-digit zero-padded) and
util.accession (0000320193-23-000106). Filings are keyed by (cik, accession) because one
accession can list several co-registrants (relevant to the 8-K 1.03 subsidiary issue).config/models.yaml (a test fails if a model
ID appears in Python); event codes, SIC exclusions and label rules live in
config/domain/finance/.models.yaml assumes Voyage
(voyage-3.5, 1024 dims, matching curated.filing_chunk.embedding). Confirm before Phase 5.BAMLH0A0HYM2; BAA10Y replaces it
(SPEC.md 3.9).