Update manifest.json with new file metadata and hashes

- Updated timestamps and hashes for backend/app/imports/router.py and backend/app/imports/service.py to reflect recent changes.
- Updated metadata for backend/tests/test_imports.py with new timestamps and hashes.
- Added new entries for .kilo/plugins/graphify.js and AGENTS.md, including their respective timestamps and hashes.
This commit is contained in:
k1nq
2026-09-21 15:55:09 +05:00
parent 35b9ea8305
commit 2117029071
14 changed files with 83324 additions and 4383 deletions
+30 -1
View File
@@ -109,8 +109,24 @@ def detail(document_id: uuid.UUID, db: Db, context: Context) -> dict:
base = summary(document) base = summary(document)
reconciliation = raw.get("reconciliation") or {} reconciliation = raw.get("reconciliation") or {}
rows = [] rows = []
pending_candidate_row_ids: set[uuid.UUID] = set()
for item in raw["records"]: for item in raw["records"]:
hold = item.get("hold_resolution") hold = item.get("hold_resolution")
candidates = [
{
"id": candidate["id"],
"kind": candidate["kind"],
"confidence": candidate["confidence"],
"resolution": candidate["resolution"],
"existingOperationId": candidate["existing_operation_id"],
}
for candidate in item.get("candidates", [])
]
has_pending_candidate = any(
candidate["resolution"] == "pending" for candidate in candidates
)
if has_pending_candidate:
pending_candidate_row_ids.add(item["id"])
rows.append( rows.append(
{ {
"id": item["id"], "id": item["id"],
@@ -122,8 +138,11 @@ def detail(document_id: uuid.UUID, db: Db, context: Context) -> dict:
"description": item["description"], "description": item["description"],
"payeeHint": None, "payeeHint": None,
"warnings": (item["warnings"] or {}).get("codes", []), "warnings": (item["warnings"] or {}).get("codes", []),
"decision": "pending" if item["critical"] else item["review_decision"], "decision": "pending"
if item["critical"] or has_pending_candidate
else item["review_decision"],
"duplicateOfOperationId": item["duplicate_operation_id"], "duplicateOfOperationId": item["duplicate_operation_id"],
"candidates": candidates,
"holdResolution": None "holdResolution": None
if hold is None if hold is None
else { else {
@@ -141,6 +160,15 @@ def detail(document_id: uuid.UUID, db: Db, context: Context) -> dict:
for item in raw["records"] for item in raw["records"]
if item["critical"] if item["critical"]
] ]
blockers.extend(
{
"code": "IMPORT_DECISION_REQUIRED",
"message": "Possible duplicate or transfer requires a decision",
"rowId": str(item["id"]),
}
for item in raw["records"]
if item["id"] in pending_candidate_row_ids
)
status = ( status = (
"matched" "matched"
if reconciliation.get("full") if reconciliation.get("full")
@@ -176,6 +204,7 @@ def detail(document_id: uuid.UUID, db: Db, context: Context) -> dict:
"discard": sum(row["decision"] == "discard" for row in rows), "discard": sum(row["decision"] == "discard" for row in rows),
"duplicate": sum(row["decision"] == "duplicate" for row in rows), "duplicate": sum(row["decision"] == "duplicate" for row in rows),
"transfer": sum(row["decision"] == "transfer" for row in rows), "transfer": sum(row["decision"] == "transfer" for row in rows),
"pending": sum(row["decision"] == "pending" for row in rows),
}, },
} }
+69 -19
View File
@@ -229,41 +229,61 @@ def _build_candidates(db: Session, *, document: StatementDocument, run: ParseRun
continue continue
duplicate = None duplicate = None
if source.external_id: if source.external_id:
matches = ( match_query = (
db.query(SourceRecord) db.query(SourceRecord)
.join(ParseRun, ParseRun.id == SourceRecord.parse_run_id) .join(ParseRun, ParseRun.id == SourceRecord.parse_run_id)
.join(StatementDocument, StatementDocument.id == SourceRecord.document_id)
.join(SourceRecordLink, SourceRecordLink.source_record_id == SourceRecord.id)
.filter( .filter(
SourceRecord.space_id == document.space_id, SourceRecord.space_id == document.space_id,
SourceRecord.document_id != document.id,
SourceRecord.id != source.id, SourceRecord.id != source.id,
SourceRecord.external_id == source.external_id, SourceRecord.external_id == source.external_id,
SourceRecord.bank_status == "posted", SourceRecord.bank_status == "posted",
ParseRun.adapter_id == run.adapter_id, ParseRun.adapter_id == run.adapter_id,
StatementDocument.status == "confirmed",
StatementDocument.active_parse_run_id == SourceRecord.parse_run_id,
) )
.all()
) )
if document.account_id is not None:
match_query = match_query.filter(
StatementDocument.account_id == document.account_id
)
matches = match_query.all()
duplicate = matches[0] if len(matches) == 1 else None duplicate = matches[0] if len(matches) == 1 else None
if duplicate is None: if duplicate is None:
matches = ( match_query = (
db.query(SourceRecord) db.query(SourceRecord)
.join(ParseRun, ParseRun.id == SourceRecord.parse_run_id)
.join(StatementDocument, StatementDocument.id == SourceRecord.document_id)
.join(SourceRecordLink, SourceRecordLink.source_record_id == SourceRecord.id)
.filter( .filter(
SourceRecord.space_id == document.space_id, SourceRecord.space_id == document.space_id,
SourceRecord.document_id != document.id,
SourceRecord.id != source.id, SourceRecord.id != source.id,
SourceRecord.record_fp == source.record_fp, SourceRecord.record_fp == source.record_fp,
SourceRecord.direction == source.direction, SourceRecord.direction == source.direction,
SourceRecord.bank_status == "posted", SourceRecord.bank_status == "posted",
ParseRun.adapter_id == run.adapter_id,
StatementDocument.status == "confirmed",
StatementDocument.active_parse_run_id == SourceRecord.parse_run_id,
) )
.all()
) )
if document.account_id is not None:
match_query = match_query.filter(
StatementDocument.account_id == document.account_id
)
matches = match_query.all()
duplicate = matches[0] if len(matches) == 1 else None duplicate = matches[0] if len(matches) == 1 else None
if duplicate is not None: if duplicate is not None:
link = db.query(SourceRecordLink).filter_by(source_record_id=duplicate.id).one_or_none() link = db.query(SourceRecordLink).filter_by(source_record_id=duplicate.id).one()
db.add( db.add(
DedupCandidate( DedupCandidate(
space_id=document.space_id, space_id=document.space_id,
source_record_id=source.id, source_record_id=source.id,
candidate_kind="duplicate", candidate_kind="duplicate",
existing_source_record_id=duplicate.id, existing_source_record_id=duplicate.id,
existing_operation_id=link.operation_id if link else None, existing_operation_id=link.operation_id,
confidence="strong" if source.external_id else "possible", confidence="strong" if source.external_id else "possible",
) )
) )
@@ -272,20 +292,28 @@ def _build_candidates(db: Session, *, document: StatementDocument, run: ParseRun
if source.occurred_on is None: if source.occurred_on is None:
continue continue
opposite = "in" if source.direction == "out" else "out" opposite = "in" if source.direction == "out" else "out"
transfer_matches = ( transfer_query = (
db.query(SourceRecord) db.query(SourceRecord)
.join(SourceRecordLink, SourceRecordLink.source_record_id == SourceRecord.id) .join(SourceRecordLink, SourceRecordLink.source_record_id == SourceRecord.id)
.join(StatementDocument, StatementDocument.id == SourceRecord.document_id)
.filter( .filter(
SourceRecord.space_id == document.space_id, SourceRecord.space_id == document.space_id,
SourceRecord.document_id != document.id,
SourceRecord.id != source.id, SourceRecord.id != source.id,
SourceRecord.amount_kopecks == source.amount_kopecks, SourceRecord.amount_kopecks == source.amount_kopecks,
SourceRecord.direction == opposite, SourceRecord.direction == opposite,
SourceRecord.bank_status == "posted", SourceRecord.bank_status == "posted",
SourceRecord.occurred_on >= source.occurred_on - timedelta(days=1), SourceRecord.occurred_on >= source.occurred_on - timedelta(days=1),
SourceRecord.occurred_on <= source.occurred_on + timedelta(days=1), SourceRecord.occurred_on <= source.occurred_on + timedelta(days=1),
StatementDocument.status == "confirmed",
StatementDocument.active_parse_run_id == SourceRecord.parse_run_id,
) )
.all()
) )
if document.account_id is not None:
transfer_query = transfer_query.filter(
StatementDocument.account_id != document.account_id
)
transfer_matches = transfer_query.all()
if len(transfer_matches) == 1: if len(transfer_matches) == 1:
counterpart = transfer_matches[0] counterpart = transfer_matches[0]
link = db.query(SourceRecordLink).filter_by(source_record_id=counterpart.id).one() link = db.query(SourceRecordLink).filter_by(source_record_id=counterpart.id).one()
@@ -504,14 +532,15 @@ def document_detail(db: Session, *, space_id: uuid.UUID, document_id: uuid.UUID)
.order_by(SourceRecord.created_at) .order_by(SourceRecord.created_at)
.all() .all()
) )
hold_ids = [record.id for record in records] record_ids = [record.id for record in records]
resolutions: dict[uuid.UUID, dict[str, Any]] = {} resolutions: dict[uuid.UUID, dict[str, Any]] = {}
if hold_ids: candidates: dict[uuid.UUID, list[dict[str, Any]]] = {}
if record_ids:
for item in ( for item in (
db.query(HoldResolution) db.query(HoldResolution)
.filter( .filter(
HoldResolution.space_id == space_id, HoldResolution.space_id == space_id,
HoldResolution.hold_source_record_id.in_(hold_ids), HoldResolution.hold_source_record_id.in_(record_ids),
) )
.all() .all()
): ):
@@ -523,6 +552,24 @@ def document_detail(db: Session, *, space_id: uuid.UUID, document_id: uuid.UUID)
"reason": item.reason, "reason": item.reason,
"resolved_at": item.resolved_at, "resolved_at": item.resolved_at,
} }
for candidate in (
db.query(DedupCandidate)
.filter(
DedupCandidate.space_id == space_id,
DedupCandidate.source_record_id.in_(record_ids),
)
.order_by(DedupCandidate.created_at)
.all()
):
candidates.setdefault(candidate.source_record_id, []).append(
{
"id": candidate.id,
"kind": candidate.candidate_kind,
"confidence": candidate.confidence,
"resolution": candidate.resolution,
"existing_operation_id": candidate.existing_operation_id,
}
)
return { return {
"id": document.id, "id": document.id,
"status": document.status, "status": document.status,
@@ -546,6 +593,7 @@ def document_detail(db: Session, *, space_id: uuid.UUID, document_id: uuid.UUID)
"review_decision": item.review_decision, "review_decision": item.review_decision,
"duplicate_operation_id": item.duplicate_operation_id, "duplicate_operation_id": item.duplicate_operation_id,
"hold_resolution": resolutions.get(item.id), "hold_resolution": resolutions.get(item.id),
"candidates": candidates.get(item.id, []),
} }
for item in records for item in records
], ],
@@ -783,15 +831,8 @@ def confirm_document(
db.refresh(document, with_for_update=True) db.refresh(document, with_for_update=True)
if document.status == "confirmed": if document.status == "confirmed":
return document return document
if document.status != "ready" or document.account_id is None: if document.status not in {"ready", "review_required"} or document.account_id is None:
raise conflict("IMPORT_NOT_READY", "Import review is not complete") raise conflict("IMPORT_NOT_READY", "Import review is not complete")
reconciliation = document.reconciliation or {}
if not reconciliation.get("full") and not reconciliation.get(
"confirmed_without_full_reconciliation"
):
raise conflict("IMPORT_RECONCILIATION_REQUIRED", "Reconciliation must be confirmed")
if reconciliation.get("matched") is False:
raise conflict("IMPORT_RECONCILIATION_MISMATCH", "Statement arithmetic does not reconcile")
pending = ( pending = (
db.query(DedupCandidate.id) db.query(DedupCandidate.id)
.join(SourceRecord, SourceRecord.id == DedupCandidate.source_record_id) .join(SourceRecord, SourceRecord.id == DedupCandidate.source_record_id)
@@ -806,6 +847,15 @@ def confirm_document(
records = db.query(SourceRecord).filter_by(parse_run_id=document.active_parse_run_id).all() records = db.query(SourceRecord).filter_by(parse_run_id=document.active_parse_run_id).all()
if any(item.critical for item in records): if any(item.critical for item in records):
raise conflict("IMPORT_FIELDS_UNRESOLVED", "Critical source fields remain unresolved") raise conflict("IMPORT_FIELDS_UNRESOLVED", "Critical source fields remain unresolved")
reconciliation = document.reconciliation or {}
if not reconciliation.get("full") and not reconciliation.get(
"confirmed_without_full_reconciliation"
):
raise conflict("IMPORT_RECONCILIATION_REQUIRED", "Reconciliation must be confirmed")
if reconciliation.get("matched") is False:
raise conflict("IMPORT_RECONCILIATION_MISMATCH", "Statement arithmetic does not reconcile")
if document.status != "ready":
raise conflict("IMPORT_NOT_READY", "Import review is not complete")
run = db.get(ParseRun, document.active_parse_run_id) run = db.get(ParseRun, document.active_parse_run_id)
if run is not None: if run is not None:
_apply_hold_matches( _apply_hold_matches(
+255
View File
@@ -1,12 +1,14 @@
import uuid import uuid
from contextlib import contextmanager from contextlib import contextmanager
from datetime import date from datetime import date
from types import SimpleNamespace
import pytest import pytest
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.errors import AppError from app.errors import AppError
from app.identity.models import User from app.identity.models import User
from app.imports import router as imports_router
from app.imports import service from app.imports import service
from app.imports.adapters.base import SourceRow from app.imports.adapters.base import SourceRow
from app.imports.models import ( from app.imports.models import (
@@ -217,6 +219,259 @@ def test_duplicate_candidate_can_reuse_unique_operation_without_client_id(db: Se
assert db.query(Operation).filter_by(space_id=space.id).count() == 1 assert db.query(Operation).filter_by(space_id=space.id).count() == 1
def test_duplicate_candidates_ignore_rows_from_same_parse_run(db: Session) -> None:
user, space, document = ready_document(db)
service.confirm_document(db, space_id=space.id, document_id=document.id, user=user)
first_source = (
db.query(SourceRecord).filter_by(parse_run_id=document.active_parse_run_id).one()
)
existing_link = db.query(SourceRecordLink).filter_by(source_record_id=first_source.id).one()
second_source = SourceRecord(
space_id=first_source.space_id,
document_id=document.id,
parse_run_id=document.active_parse_run_id,
occurred_on=first_source.occurred_on,
amount_kopecks=first_source.amount_kopecks,
direction=first_source.direction,
row_ref="sheet0:row3",
record_fp=first_source.record_fp,
)
db.add(second_source)
db.flush()
db.add(
SourceRecordLink(
space_id=space.id,
source_record_id=second_source.id,
operation_id=existing_link.operation_id,
kind="duplicate",
)
)
db.flush()
run = db.get(ParseRun, document.active_parse_run_id)
assert run is not None
assert service._build_candidates(db, document=document, run=run) is False
assert db.query(DedupCandidate).filter_by(space_id=document.space_id).count() == 0
def test_duplicate_candidates_ignore_inactive_parse_runs(db: Session) -> None:
user, space, source_document = ready_document(db)
service.confirm_document(db, space_id=space.id, document_id=source_document.id, user=user)
active_source = (
db.query(SourceRecord).filter_by(parse_run_id=source_document.active_parse_run_id).one()
)
active_link = db.query(SourceRecordLink).filter_by(source_record_id=active_source.id).one()
inactive_run = ParseRun(
space_id=space.id,
document_id=source_document.id,
adapter_id="alfa_debit_xlsx",
status="succeeded",
)
db.add(inactive_run)
db.flush()
inactive_source = SourceRecord(
space_id=space.id,
document_id=source_document.id,
parse_run_id=inactive_run.id,
occurred_on=active_source.occurred_on,
amount_kopecks=active_source.amount_kopecks,
direction=active_source.direction,
row_ref="sheet0:old-row",
record_fp="inactive-run-fingerprint",
)
db.add(inactive_source)
db.flush()
db.add(
SourceRecordLink(
space_id=space.id,
source_record_id=inactive_source.id,
operation_id=active_link.operation_id,
kind="duplicate",
)
)
target_document = StatementDocument(
space_id=space.id,
account_id=source_document.account_id,
original_filename="target.xlsx",
size_bytes=100,
content_sha256=uuid.uuid4().hex + uuid.uuid4().hex,
object_key=uuid.uuid4().hex,
status="review_required",
reconciliation={"full": True, "matched": True},
)
db.add(target_document)
db.flush()
target_run = ParseRun(
space_id=space.id,
document_id=target_document.id,
adapter_id="alfa_debit_xlsx",
status="succeeded",
)
db.add(target_run)
db.flush()
target_document.active_parse_run_id = target_run.id
db.add(
SourceRecord(
space_id=space.id,
document_id=target_document.id,
parse_run_id=target_run.id,
occurred_on=inactive_source.occurred_on,
amount_kopecks=inactive_source.amount_kopecks,
direction=inactive_source.direction,
row_ref="sheet0:row2",
record_fp=inactive_source.record_fp,
)
)
db.flush()
assert service._build_candidates(db, document=target_document, run=target_run) is False
assert db.query(DedupCandidate).filter_by(space_id=space.id).count() == 0
def test_duplicate_candidates_ignore_unconfirmed_documents(db: Session) -> None:
_user, space, first_document = ready_document(db)
first_source = (
db.query(SourceRecord).filter_by(parse_run_id=first_document.active_parse_run_id).one()
)
second_document = StatementDocument(
space_id=space.id,
account_id=first_document.account_id,
original_filename="second.xlsx",
size_bytes=100,
content_sha256=uuid.uuid4().hex + uuid.uuid4().hex,
object_key=uuid.uuid4().hex,
status="review_required",
reconciliation={"full": True, "matched": True},
)
db.add(second_document)
db.flush()
second_run = ParseRun(
space_id=space.id,
document_id=second_document.id,
adapter_id="alfa_debit_xlsx",
status="succeeded",
)
db.add(second_run)
db.flush()
second_document.active_parse_run_id = second_run.id
db.add(
SourceRecord(
space_id=space.id,
document_id=second_document.id,
parse_run_id=second_run.id,
occurred_on=first_source.occurred_on,
amount_kopecks=first_source.amount_kopecks,
direction=first_source.direction,
row_ref="sheet0:row2",
record_fp=first_source.record_fp,
)
)
db.flush()
assert service._build_candidates(db, document=second_document, run=second_run) is False
assert db.query(DedupCandidate).filter_by(space_id=space.id).count() == 0
def test_duplicate_candidates_are_retained_before_account_mapping(db: Session) -> None:
user, space, first_document = ready_document(db)
service.confirm_document(db, space_id=space.id, document_id=first_document.id, user=user)
first_source = (
db.query(SourceRecord).filter_by(parse_run_id=first_document.active_parse_run_id).one()
)
second_document = StatementDocument(
space_id=space.id,
account_id=None,
original_filename="unmapped.xlsx",
size_bytes=100,
content_sha256=uuid.uuid4().hex + uuid.uuid4().hex,
object_key=uuid.uuid4().hex,
status="review_required",
reconciliation={"full": True, "matched": True},
)
db.add(second_document)
db.flush()
second_run = ParseRun(
space_id=space.id,
document_id=second_document.id,
adapter_id="alfa_debit_xlsx",
status="succeeded",
)
db.add(second_run)
db.flush()
second_document.active_parse_run_id = second_run.id
source = SourceRecord(
space_id=space.id,
document_id=second_document.id,
parse_run_id=second_run.id,
occurred_on=first_source.occurred_on,
amount_kopecks=first_source.amount_kopecks,
direction=first_source.direction,
row_ref="sheet0:row2",
record_fp=first_source.record_fp,
)
db.add(source)
db.flush()
assert service._build_candidates(db, document=second_document, run=second_run) is True
candidate = db.query(DedupCandidate).filter_by(source_record_id=source.id).one()
assert candidate.existing_source_record_id == first_source.id
def test_import_detail_exposes_pending_candidate_and_blocker(db: Session) -> None:
_user, space, document = ready_document(db)
document.status = "review_required"
source = db.query(SourceRecord).filter_by(parse_run_id=document.active_parse_run_id).one()
candidate = DedupCandidate(
space_id=space.id,
source_record_id=source.id,
candidate_kind="duplicate",
confidence="possible",
)
db.add(candidate)
db.flush()
response = imports_router.detail(document.id, db, SimpleNamespace(space=space))
assert response["rows"][0]["decision"] == "pending"
assert response["rows"][0]["candidates"] == [
{
"id": candidate.id,
"kind": "duplicate",
"confidence": "possible",
"resolution": "pending",
"existingOperationId": None,
}
]
assert response["blockers"] == [
{
"code": "IMPORT_DECISION_REQUIRED",
"message": "Possible duplicate or transfer requires a decision",
"rowId": str(source.id),
}
]
assert response["totals"]["pending"] == 1
def test_confirm_reports_pending_candidate_instead_of_generic_not_ready(db: Session) -> None:
user, space, document = ready_document(db)
document.status = "review_required"
source = db.query(SourceRecord).filter_by(parse_run_id=document.active_parse_run_id).one()
db.add(
DedupCandidate(
space_id=space.id,
source_record_id=source.id,
candidate_kind="duplicate",
confidence="possible",
)
)
db.flush()
with pytest.raises(AppError) as exc:
service.confirm_document(db, space_id=space.id, document_id=document.id, user=user)
assert exc.value.code == "IMPORT_DECISIONS_REQUIRED"
def test_transfer_merge_revert_restores_counterpart_operation(db: Session) -> None: def test_transfer_merge_revert_restores_counterpart_operation(db: Session) -> None:
user, space, first_document = ready_document(db) user, space, first_document = ready_document(db)
service.confirm_document(db, space_id=space.id, document_id=first_document.id, user=user) service.confirm_document(db, space_id=space.id, document_id=first_document.id, user=user)
+17790 -5
View File
File diff suppressed because it is too large Load Diff
+21 -17
View File
@@ -1,35 +1,35 @@
{ {
"0": "identity/router.py", "0": "identity/service.py",
"1": "budgeting/router.py", "1": "budgeting/router.py",
"2": "Base", "2": "Base",
"3": "set_space_context", "3": "set_space_context",
"4": "imports/router.py", "4": "imports/router.py",
"5": "alfa_xlsx.py", "5": "alfa_xlsx.py",
"6": "Session", "6": "ledger/service.py",
"7": "types.ts", "7": "types.ts",
"8": "router.tsx", "8": "router.tsx",
"9": "ledger/router.py", "9": "ledger/router.py",
"10": "AccountsPage.tsx", "10": "main.py",
"11": "compilerOptions", "11": "compilerOptions",
"12": "AnalyticsPage.tsx", "12": "AnalyticsPage.tsx",
"13": "analytics/router.py", "13": "exports/router.py",
"14": "devDependencies", "14": "devDependencies",
"15": "test_import_holds.py", "15": "Session",
"16": "dependencies", "16": "dependencies",
"17": "ImportDetailPage.tsx", "17": "ImportDetailPage.tsx",
"18": "OperationsPage.tsx", "18": "OperationsPage.tsx",
"19": "SettingsPage.tsx", "19": "useOperations.ts",
"20": "Toaster.tsx", "20": "Toaster.tsx",
"21": "ozon_pdf.py", "21": "adapters/base.py",
"22": "BudgetPage.tsx", "22": "BudgetPage.tsx",
"23": "client.ts", "23": "client.ts",
"24": "fields.tsx", "24": "AccountsPage.tsx",
"25": "Docker Compose Development Stack", "25": "Docker Compose Development Stack",
"26": "Financial Space", "26": "Financial Space",
"27": "scripts", "27": "scripts",
"28": "dependencies.py", "28": "exports/service.py",
"29": "General Pool", "29": "General Pool",
"30": "TestBaseContract", "30": "test_adapter_contracts.py",
"31": "Per-File Envelope Encryption", "31": "Per-File Envelope Encryption",
"32": "Operation", "32": "Operation",
"33": "Versioned Statement Imports", "33": "Versioned Statement Imports",
@@ -37,7 +37,7 @@
"35": "Closed Beta Launch Gates", "35": "Closed Beta Launch Gates",
"36": "package.json", "36": "package.json",
"37": "Financial Tracker MVP", "37": "Financial Tracker MVP",
"38": "test_adapter_contracts.py", "38": "TestRocketbankDebitContract",
"39": "generate-api.mjs", "39": "generate-api.mjs",
"40": "schema.d.ts", "40": "schema.d.ts",
"41": "test_api.py", "41": "test_api.py",
@@ -49,7 +49,7 @@
"47": "graphify", "47": "graphify",
"48": "@types/react-dom", "48": "@types/react-dom",
"49": "typescript-eslint", "49": "typescript-eslint",
"50": "SourceRow", "50": "OzonDebitPdfAdapter",
"51": "vitest", "51": "vitest",
"52": "Favicon SVG (ledger lines icon)", "52": "Favicon SVG (ledger lines icon)",
"53": "init-app-role.sh", "53": "init-app-role.sh",
@@ -62,7 +62,7 @@
"60": "vite.config.ts", "60": "vite.config.ts",
"61": "finance-backend", "61": "finance-backend",
"62": "LocalDirectoryBlobStore", "62": "LocalDirectoryBlobStore",
"63": "account_fingerprint", "63": "TestOzonDebitContract",
"64": "Gitea CI and real statement imports", "64": "Gitea CI and real statement imports",
"65": "20260918_0002_hold_lifecycle.py", "65": "20260918_0002_hold_lifecycle.py",
"66": "eslint-plugin-react-hooks", "66": "eslint-plugin-react-hooks",
@@ -70,8 +70,12 @@
"68": "@testing-library/dom", "68": "@testing-library/dom",
"69": "@testing-library/react", "69": "@testing-library/react",
"70": "vite", "70": "vite",
"71": "spaces/router.py", "71": "ColumnChart.tsx",
"72": "identity/service.py", "72": "conftest.py",
"73": "get_settings", "73": "imports/service.py",
"74": "ColumnChart.tsx" "74": "BlobStore",
"75": "AppError",
"76": "celery_app.py",
"77": "AGENTS.md",
"78": "graphify.js"
} }
+1
View File
@@ -0,0 +1 @@
{"0": "5be265b1520d78ac", "1": "202b56dfc67b2426", "2": "e8067d100b0e48e0", "3": "a199479974543630", "4": "70c8a604a894445d", "5": "092f91ab80789a1d", "6": "3dc6d0d50528ba98", "7": "478b34eb0402fa7d", "8": "c73ee613d6cc4603", "9": "5ebc227112f367f4", "10": "a91e7c0bdb81ca71", "11": "f039d542469fefc7", "12": "0f21d202d861324c", "13": "c90eb017c5213760", "14": "603fe8eab561d17f", "15": "115e19466baf98c7", "16": "1ff1ee70001a89de", "17": "1dd8594011ea2d4f", "18": "8d1c055c213ab51a", "19": "806f8f9f67c1321f", "20": "ba5d835cd3756f81", "21": "bedd3bc934b83f58", "22": "e55afacc55f53152", "23": "cf7c706bee4e29cc", "24": "345cd1b7ec82598d", "25": "f01c92b243645b4e", "26": "54c3380f579b6729", "27": "d8d767c824110809", "28": "ad3dccb7bd56195e", "29": "3147966ac3ee3b0f", "30": "5c32e2734dd85352", "31": "099ee7edfccfb30f", "32": "0778a13a682f0cbb", "33": "6be20de557b963e9", "34": "da7b9f31790187c3", "35": "17bba31989deabe4", "36": "267c43d4a566a343", "37": "cff206ec60c41144", "38": "c9cd0ce5f2cf7cba", "39": "0e87c765d5713ad6", "40": "ce680f01cc87ac2b", "41": "24380e4087fb70d5", "42": "c58209407d4237e8", "43": "54905c779f6306ec", "44": "bdb0b28856af8fe2", "45": "e5d51caa27a2a995", "46": "12858d9498b8f2dc", "47": "2f8c667574e48967", "48": "08332675f638af21", "49": "4ed4ad08eaab7c1c", "50": "354367198161115c", "51": "ba6724f5637cd5d9", "52": "fdf8b4d4bf2be849", "53": "66f8ffc9c3fbedf2", "54": "73723cec09ed2417", "55": "f9e8dac18e2e7bdf", "56": "86619fe91fc50452", "57": "8a3d1d863ddb4d2f", "58": "acee339eea91405a", "59": "99c0fcbdc4f53a0e", "60": "cd42295e6b9fcac1", "61": "3712295d0a11267c", "62": "f787d08450bb4ac6", "63": "c0e8c155ec6fcd84", "64": "fa2cf116b4b9871b", "65": "f3a0edb87b006f25", "66": "a0aa708a9e56bca1", "67": "99286ea0671b6a66", "68": "14861c2ceee47357", "69": "2735bfc7a6304c2e", "70": "e049e07b5afc08cf", "71": "c11d4d615ce6b927", "72": "70d2f844bf68b0d0", "73": "c95100868c3ef766", "74": "17c53201f17a093a", "75": "40bdab93c3490c2a", "76": "7ab7989194b62fca", "77": "97ec841e4f367258", "78": "c9051bc3ad4170f1"}
@@ -0,0 +1,81 @@
{
"0": "get_settings",
"1": "budgeting/router.py",
"2": "Base",
"3": "set_space_context",
"4": "imports/router.py",
"5": "alfa_xlsx.py",
"6": "imports/service.py",
"7": "types.ts",
"8": "router.tsx",
"9": "ledger/router.py",
"10": "AccountsPage.tsx",
"11": "compilerOptions",
"12": "AnalyticsPage.tsx",
"13": "SettingsPage.tsx",
"14": "devDependencies",
"15": "Session",
"16": "dependencies",
"17": "ImportDetailPage.tsx",
"18": "OperationsPage.tsx",
"19": "queryKeys.ts",
"20": "Toaster.tsx",
"21": "adapters/base.py",
"22": "BudgetPage.tsx",
"23": "client.ts",
"24": "fields.tsx",
"25": "Docker Compose Development Stack",
"26": "Financial Space",
"27": "scripts",
"28": "dependencies.py",
"29": "General Pool",
"30": "test_adapter_contracts.py",
"31": "Per-File Envelope Encryption",
"32": "Operation",
"33": "Versioned Statement Imports",
"34": "Finance Frontend SPA",
"35": "Closed Beta Launch Gates",
"36": "package.json",
"37": "Financial Tracker MVP",
"38": "TestRocketbankDebitContract",
"39": "generate-api.mjs",
"40": "schema.d.ts",
"41": "test_api.py",
"42": "i18n-parity.test.ts",
"43": "Expected Income",
"44": "test_private_fixtures.py",
"45": "TestAlfaDebitContract",
"46": "jsdom",
"47": "graphify",
"48": "@types/react-dom",
"49": "typescript-eslint",
"50": "rocketbank_pdf.py",
"51": "vitest",
"52": "Favicon SVG (ledger lines icon)",
"53": "init-app-role.sh",
"54": "app/__init__.py",
"55": "spaces/__init__.py",
"56": "Payee",
"57": "eslint.config.js",
"58": "money.test.ts",
"59": "vite-env.d.ts",
"60": "vite.config.ts",
"61": "finance-backend",
"62": "LocalDirectoryBlobStore",
"63": "TestOzonDebitContract",
"64": "Gitea CI and real statement imports",
"65": "20260918_0002_hold_lifecycle.py",
"66": "eslint-plugin-react-hooks",
"67": "openapi-typescript",
"68": "@testing-library/dom",
"69": "@testing-library/react",
"70": "vite",
"71": "spaces/router.py",
"72": "identity/service.py",
"73": "crypto.py",
"74": "current_session",
"75": "AppError",
"76": "me",
"77": "AGENTS.md",
"78": "graphify.js"
}
+370
View File
@@ -0,0 +1,370 @@
# Graph Report - finance (2026-09-21)
## Corpus Check
- 151 files · ~64,579 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 1275 nodes · 3720 edges · 79 communities (56 shown, 16 thin omitted)
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 71 edges (avg confidence: 0.93)
- Token cost: 0 input · 0 output
## Graph Freshness
- Built from commit: `35b9ea83`
- Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation)
- get_settings
- budgeting/router.py
- Base
- set_space_context
- imports/router.py
- alfa_xlsx.py
- imports/service.py
- types.ts
- router.tsx
- ledger/router.py
- AccountsPage.tsx
- compilerOptions
- AnalyticsPage.tsx
- SettingsPage.tsx
- devDependencies
- Session
- dependencies
- ImportDetailPage.tsx
- OperationsPage.tsx
- queryKeys.ts
- Toaster.tsx
- adapters/base.py
- BudgetPage.tsx
- client.ts
- fields.tsx
- Docker Compose Development Stack
- Financial Space
- scripts
- dependencies.py
- General Pool
- test_adapter_contracts.py
- Per-File Envelope Encryption
- Operation
- Versioned Statement Imports
- Finance Frontend SPA
- Closed Beta Launch Gates
- package.json
- Financial Tracker MVP
- TestRocketbankDebitContract
- generate-api.mjs
- schema.d.ts
- test_api.py
- i18n-parity.test.ts
- Expected Income
- test_private_fixtures.py
- TestAlfaDebitContract
- jsdom
- graphify
- @types/react-dom
- typescript-eslint
- rocketbank_pdf.py
- vitest
- Favicon SVG (ledger lines icon)
- init-app-role.sh
- Payee
- finance-backend
- LocalDirectoryBlobStore
- TestOzonDebitContract
- Gitea CI and real statement imports
- 20260918_0002_hold_lifecycle.py
- eslint-plugin-react-hooks
- openapi-typescript
- @testing-library/dom
- @testing-library/react
- vite
- spaces/router.py
- identity/service.py
- crypto.py
- current_session
- AppError
- me
- AGENTS.md
## God Nodes (most connected - your core abstractions)
1. `Session` - 144 edges
2. `AppError` - 54 edges
3. `Base` - 47 edges
4. `PkMixin` - 39 edges
5. `get_settings()` - 38 edges
6. `TimestampMixin` - 38 edges
7. `conflict()` - 35 edges
8. `record()` - 34 edges
9. `UnsupportedStatementLayout` - 30 edges
10. `StatementDocument` - 30 edges
## Surprising Connections (you probably didn't know these)
- `Typed Same-Origin API Client` --conceptually_related_to--> `Same-Origin Reverse Proxy` [INFERRED]
frontend/README.md → infra/README.md
- `Financial Space Ownership` --conceptually_related_to--> `Financial Space` [INFERRED]
docs/adr/0001-financial-space-ownership-and-rls.md → CONTEXT.md
- `Versioned Statement Imports` --conceptually_related_to--> `Statement Import` [INFERRED]
docs/adr/0003-versioned-statement-imports.md → CONTEXT.md
- `MVP Release Gates` --conceptually_related_to--> `Closed Beta Launch Gates` [INFERRED]
.kilo/plans/1789034192214-financial-tracker-mvp.md → docs/operations/beta-release-runbook.md
- `Carry-Over Budget Reserves` --references--> `Savings Envelope` [EXTRACTED]
docs/adr/0002-journal-and-budget-reserves.md → CONTEXT.md
## Import Cycles
- None detected.
## Hyperedges (group relationships)
- **Auditable Financial Model** — context_operation, context_posting, docs_adr_0002_journal_and_budget_reserves_derived_balances, docs_adr_0002_journal_and_budget_reserves_plan_funding_separation, docs_adr_0002_journal_and_budget_reserves_closure_snapshots [EXTRACTED 1.00]
- **Financial Space Tenancy Boundary** — context_financial_space, context_membership, docs_adr_0001_financial_space_ownership_and_rls_defense_in_depth_authorization, docs_adr_0001_financial_space_ownership_and_rls_transaction_local_rls, docs_adr_0001_financial_space_ownership_and_rls_cross_space_integrity [EXTRACTED 1.00]
- **Safe Statement Import Flow** — context_statement_import, context_parse_run, context_source_record, docs_adr_0003_versioned_statement_imports_immutable_statement_document, docs_adr_0003_versioned_statement_imports_conservative_deduplication, docs_adr_0003_versioned_statement_imports_atomic_confirm_revert [EXTRACTED 1.00]
## Communities (79 total, 16 thin omitted)
### Community 0 - "get_settings"
Cohesion: 0.14
Nodes (27): get_settings(), Settings, send_token_email(), accept_invite(), AcceptInviteBody, confirm_password_reset(), create_invite(), InviteBody (+19 more)
### Community 1 - "budgeting/router.py"
Cohesion: 0.14
Nodes (42): balances(), cash_flow(), category_spend(), plan_fact(), Context, date, Db, get (+34 more)
### Community 2 - "Base"
Cohesion: 0.07
Nodes (82): downgrade(), upgrade(), AuditEvent, Append-only domain/security event. Never stores raw financial payloads., ExpectedIncome, ExpectedIncomeMatch, FundingMovement, PeriodClosure (+74 more)
### Community 3 - "set_space_context"
Cohesion: 0.07
Nodes (44): get_db(), get_engine(), get_session_factory(), Any, sessionmaker, Transaction-local RLS context. No-op on SQLite (tests); fail-closed on…, Transactional scope for workers/endpoints that own the transaction lifecycle., session_scope() (+36 more)
### Community 4 - "imports/router.py"
Cohesion: 0.15
Nodes (33): confirm(), ConfirmBody, decide_row(), DecisionBody, delete_source(), detail(), HoldResolveBody, imports() (+25 more)
### Community 5 - "alfa_xlsx.py"
Cohesion: 0.17
Nodes (24): AlfaDebitXlsxAdapter, _AlfaHeaderMeta, AlfaXlsxAdapterBase, _cell_str(), _debit_find_header(), _debit_header_metadata(), _debit_header_value(), _debit_norm() (+16 more)
### Community 6 - "imports/service.py"
Cohesion: 0.08
Nodes (72): Any, record(), datetime, utcnow(), conflict(), not_found(), magic_kind(), BlobStore (+64 more)
### Community 7 - "types.ts"
Cohesion: 0.06
Nodes (50): api, Account, AccountStatus, BalanceItem, BalancesReport, BudgetBlocker, BudgetCategory, BudgetStatus (+42 more)
### Community 8 - "router.tsx"
Cohesion: 0.11
Nodes (31): AcceptInvitePayload, LoginPayload, PasswordResetPayload, AcceptInvitePage, AccountsPage, BudgetPage, LoginPage, OperationsPage (+23 more)
### Community 9 - "ledger/router.py"
Cohesion: 0.15
Nodes (31): account_json(), AccountBody, accounts(), archive_account(), create_account(), create_operation(), delete_account(), operation_json() (+23 more)
### Community 10 - "AccountsPage.tsx"
Cohesion: 0.12
Nodes (22): AccountType, ImportsPage, Button(), ButtonProps, Variant, ConfirmDialog(), ConfirmDialogProps, EmptyState() (+14 more)
### Community 11 - "compilerOptions"
Cohesion: 0.07
Nodes (28): compilerOptions, baseUrl, esModuleInterop, forceConsistentCasingInFileNames, isolatedModules, jsx, lib, module (+20 more)
### Community 12 - "AnalyticsPage.tsx"
Cohesion: 0.12
Nodes (19): AnalyticsPage, CashFlowColumn, ColumnChart(), ColumnChartProps, BarRow, HorizontalBars(), HorizontalBarsProps, PlanFactBars() (+11 more)
### Community 13 - "SettingsPage.tsx"
Cohesion: 0.23
Nodes (10): NotFoundPage, PageHeader(), PageHeaderProps, useCancelDeletion(), useRequestDeletion(), useRequestExport(), useSpaceDeletion(), DeletionForm() (+2 more)
### Community 14 - "devDependencies"
Cohesion: 0.11
Nodes (19): eslint, @eslint/js, eslint-plugin-jsx-a11y, devDependencies, eslint, @eslint/js, eslint-plugin-jsx-a11y, globals (+11 more)
### Community 15 - "Session"
Cohesion: 0.08
Nodes (88): balances(), cash_flow(), category_spend(), plan_fact(), date, UUID, BudgetPeriod, Calendar month budget period in the space timezone (month granularity). (+80 more)
### Community 16 - "dependencies"
Cohesion: 0.11
Nodes (19): dependencies, @hookform/resolvers, i18next, react, react-dom, react-hook-form, react-i18next, react-router-dom (+11 more)
### Community 17 - "ImportDetailPage.tsx"
Cohesion: 0.27
Nodes (15): RowDecision, ImportDetailPage, useToast(), useConfirmImport(), useImport(), useImportErrorToaster(), useReparseImport(), useRevertImport() (+7 more)
### Community 18 - "OperationsPage.tsx"
Cohesion: 0.17
Nodes (21): Badge(), BadgeTone, useActiveAccounts(), useImports(), useCategorizeOperation(), useCreateOperation(), useLedgerMutation(), useOperations() (+13 more)
### Community 19 - "queryKeys.ts"
Cohesion: 0.16
Nodes (13): invalidationRoots, queryKeys, SESSION_ROOT, sessionInvalidationRoot, SPACE_ROOT, spaceInvalidationRoot, CreateAccountPayload, OperationFilters (+5 more)
### Community 20 - "Toaster.tsx"
Cohesion: 0.13
Nodes (14): Locale, App(), queryClient, AppRoutes(), ToastApi, ToastContext, ToastItem, ToastKind (+6 more)
### Community 21 - "adapters/base.py"
Cohesion: 0.10
Nodes (21): account_fingerprint(), amount_to_kopecks(), _mask_digits(), money_to_kopecks(), normalize_description(), date, Parse statement money strings like '-1 234,56 RUR', '+ 500.00 ₽', '12 345'.…, record_fingerprint() (+13 more)
### Community 22 - "BudgetPage.tsx"
Cohesion: 0.36
Nodes (12): useBudgetCategories(), useBudgetErrorToaster(), useBudgetSummary(), useCloseMonth(), useCopyPreviousMonth(), useFundCategory(), usePeriodMutation(), useReopenMonth() (+4 more)
### Community 23 - "client.ts"
Cohesion: 0.23
Nodes (10): apiClient, ApiError, buildUrl(), ErrorEnvelope, parseErrorEnvelope(), QueryValue, readCsrfToken(), request() (+2 more)
### Community 24 - "fields.tsx"
Cohesion: 0.23
Nodes (10): FieldChrome, InputProps, MoneyField(), MoneyFieldProps, SelectFieldProps, SelectProps, TextareaField(), TextareaFieldProps (+2 more)
### Community 25 - "Docker Compose Development Stack"
Cohesion: 0.29
Nodes (8): Commodity Infrastructure Services, Docker Compose Development Stack, Internal Backend Network, Same-Origin Frontend Service, Worker Parsing Isolation, Finance Infrastructure, Same-Origin Reverse Proxy, Backend and Frontend Workstream Contracts
### Community 26 - "Financial Space"
Cohesion: 0.17
Nodes (12): Credit Reserve, Financial Account, Financial Space, Membership, User, Carry-Over Budget Reserves, Provider-Neutral Data Boundary, Russian Federation Data Residency (+4 more)
### Community 27 - "scripts"
Cohesion: 0.22
Nodes (9): scripts, api:generate, build, dev, lint, preview, test, test:watch (+1 more)
### Community 28 - "dependencies.py"
Cohesion: 0.19
Nodes (16): current_context(), Context, CurrentSession, Header, RequestContext, require_csrf(), forbidden(), Any (+8 more)
### Community 29 - "General Pool"
Cohesion: 0.29
Nodes (8): Budget Period, Funding, General Pool, Monthly Category, Plan Assignment, Savings Envelope, Immutable Closure Snapshots, Plan and Funding Separation
### Community 30 - "test_adapter_contracts.py"
Cohesion: 0.13
Nodes (10): AlfaCreditXlsxAdapter, Protocol, StatementAdapter, detect_adapter(), OzonCreditPdfAdapter, make_draft(), make_source_row(), Sanitized synthetic contract tests for the evidenced statement adapters. These… (+2 more)
### Community 31 - "Per-File Envelope Encryption"
Cohesion: 0.29
Nodes (7): Immutable Statement Document, Per-File Envelope Encryption, Backup and Restore Runbook, Mandatory KEK Backup, Quarterly Restore Drill, Disaster Restore Procedure, Post-Restore Deletion Replay
### Community 32 - "Operation"
Cohesion: 0.47
Nodes (6): Internal Transfer, Operation, Posting, Refund, Derived Account Balances, Operations and Postings Journal
### Community 33 - "Versioned Statement Imports"
Cohesion: 0.20
Nodes (11): Parse Run, Source Record, Statement Import, Atomic Import Confirm and Revert, Conservative Deduplication, Parser Versioning and Review Diff, Versioned Statement Imports, Layout Drift Re-Gating (+3 more)
### Community 34 - "Finance Frontend SPA"
Cohesion: 0.29
Nodes (7): Server-Side Secret Boundary, Grossbukh Application Shell, Accessible Responsive UI, Finance Frontend SPA, RU and EN Localization, Space-Scoped Query Keys, Typed Same-Origin API Client
### Community 35 - "Closed Beta Launch Gates"
Cohesion: 0.20
Nodes (11): Crypto-Shredded Deletion, Closed Beta Release Runbook, Forward-Fix Rollback Policy, Financial Data Incident Response, Closed Beta Launch Gates, Production Launch Sequence, Thirty-Day Space Deletion, Durable Deletion Ledger (+3 more)
### Community 36 - "package.json"
Cohesion: 0.40
Nodes (4): name, private, type, version
### Community 37 - "Financial Tracker MVP"
Cohesion: 0.18
Nodes (11): Audited Break-Glass Access, Cross-Space Referential Integrity, Defense-in-Depth Authorization, Financial Space Ownership, Transaction-Local RLS Context, Budget Invariants, Critical User Path, Financial Tracker MVP (+3 more)
### Community 38 - "TestRocketbankDebitContract"
Cohesion: 0.24
Nodes (3): same_page_rocket_lines(), synthetic_rocket_lines(), TestRocketbankDebitContract
### Community 39 - "generate-api.mjs"
Cohesion: 0.40
Nodes (5): firstReadable(), LOCAL_CANDIDATES, main(), OUT_DIR, REMOTE_CANDIDATES
### Community 40 - "schema.d.ts"
Cohesion: 0.33
Nodes (5): components, $defs, operations, paths, webhooks
### Community 41 - "test_api.py"
Cohesion: 0.67
Nodes (3): TestClient, test_invite_login_and_authenticated_space(), test_openapi_contains_critical_paths()
### Community 44 - "test_private_fixtures.py"
Cohesion: 0.13
Nodes (20): Any, Reconcile parsed rows against declared bank controls with stable diagnostics.…, _reconciliation_summary(), _check(), _fail(), _load_entries(), parsed(), fixture (+12 more)
### Community 45 - "TestAlfaDebitContract"
Cohesion: 0.21
Nodes (3): alfa_workbook_bytes(), synthetic_alfa_rows(), TestAlfaDebitContract
### Community 47 - "graphify"
Cohesion: 0.17
Nodes (11): command, enabled, timeout, type, mcp, graphify, plugin, $schema (+3 more)
### Community 50 - "rocketbank_pdf.py"
Cohesion: 0.27
Nodes (6): _date_from(), _PendingRecord, date, Rocketbank debit statement contract, evidenced against the real document.…, RocketbankDebitPdfAdapter, open_pdf()
### Community 62 - "LocalDirectoryBlobStore"
Cohesion: 0.39
Nodes (3): LocalDirectoryBlobStore, Path, S3-neutral local store; production swaps in an S3-compatible client.
### Community 63 - "TestOzonDebitContract"
Cohesion: 0.16
Nodes (6): minimal_pdf(), ozon_extracted(), _QuotaSettings, Build a small valid single-font PDF with empty or ASCII-only pages., TestOzonDebitContract, MonkeyPatch
### Community 64 - "Gitea CI and real statement imports"
Cohesion: 0.29
Nodes (6): Confirmed decisions, Gitea CI and real statement imports, Goal and constraints, Implementation plan, Principal risks, Validation and rollout
### Community 65 - "20260918_0002_hold_lifecycle.py"
Cohesion: 0.80
Nodes (4): downgrade(), _has_check_constraint(), _has_column(), upgrade()
### Community 71 - "spaces/router.py"
Cohesion: 0.18
Nodes (18): cancel_deletion(), current(), deletion_status(), DeletionBody, invite_member(), MemberInviteBody, alias, BaseModel (+10 more)
### Community 72 - "identity/service.py"
Cohesion: 0.28
Nodes (18): unauthorized(), accept_invitation(), authenticate(), create_invitation(), create_password_reset(), create_session(), get_active_session(), normalize_email() (+10 more)
### Community 73 - "crypto.py"
Cohesion: 0.40
Nodes (5): encrypt_file(), EncryptedFile, Per-file random DEK; DEK wrapped with the KEK from the secret manager., Crypto-shred hook: deleting wrapped DEK rows in DB plus blobs removes access., shred_dek_material()
### Community 74 - "current_session"
Cohesion: 0.29
Nodes (7): current_session(), alias, Db, User, Cookie, session_cookie_name, UserSession
### Community 75 - "AppError"
Cohesion: 0.43
Nodes (5): AppError, Domain error rendered as a machine-readable JSON error payload., unprocessable(), _quota_error(), Exception
### Community 76 - "me"
Cohesion: 0.67
Nodes (3): me(), CurrentSession, get
## Knowledge Gaps
- **186 isolated node(s):** `$schema`, `file:///home/k1nq/repos/finance/.kilo/plugins/graphify.js`, `type`, `/home/k1nq/.local/bin/graphify-mcp`, `/home/k1nq/repos/finance/graphify-out/graph.json` (+181 more)
These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 339 node(s) total have ≤1 connection when file, concept and rationale nodes are included.)
- **16 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `Session` connect `Session` to `Base`, `set_space_context`, `imports/service.py`, `identity/service.py`, `dependencies.py`?**
_High betweenness centrality (0.087) - this node is a cross-community bridge._
- **Why does `AppError` connect `AppError` to `get_settings`, `Base`, `set_space_context`, `imports/router.py`, `alfa_xlsx.py`, `imports/service.py`, `TestRocketbankDebitContract`, `identity/service.py`, `crypto.py`, `Session`, `adapters/base.py`, `dependencies.py`, `test_adapter_contracts.py`, `TestOzonDebitContract`?**
_High betweenness centrality (0.067) - this node is a cross-community bridge._
- **Why does `bad_request()` connect `Session` to `budgeting/router.py`, `imports/service.py`, `spaces/router.py`, `identity/service.py`, `ledger/router.py`, `AppError`, `dependencies.py`?**
_High betweenness centrality (0.017) - this node is a cross-community bridge._
- **Are the 2 inferred relationships involving `Session` (e.g. with `UTCDateTime` and `test_postgres_rls_requires_matching_transaction_context()`) actually correct?**
_`Session` has 2 INFERRED edges - model-reasoned connections that need verification._
- **Are the 24 inferred relationships involving `AppError` (e.g. with `request_export()` and `login()`) actually correct?**
_`AppError` has 24 INFERRED edges - model-reasoned connections that need verification._
- **Are the 3 inferred relationships involving `Base` (e.g. with `downgrade()` and `upgrade()`) actually correct?**
_`Base` has 3 INFERRED edges - model-reasoned connections that need verification._
- **What connects `$schema`, `file:///home/k1nq/repos/finance/.kilo/plugins/graphify.js`, `type` to the rest of the system?**
_186 weakly-connected nodes found - possible documentation gaps or missing edges._
File diff suppressed because it is too large Load Diff
+938
View File
@@ -0,0 +1,938 @@
{
"backend/alembic/env.py": {
"mtime": 1789129232.0159023,
"seen": 1789377303.0691774,
"ast_hash": "d78f67ad0caed3f10805122e8d8e82d0",
"semantic_hash": "d78f67ad0caed3f10805122e8d8e82d0"
},
"backend/alembic/versions/20260911_0001_initial_schema.py": {
"mtime": 1789130024.8355556,
"seen": 1789377303.0691814,
"ast_hash": "3a446ae61cbf2362d24dc9ab6e72dd2d",
"semantic_hash": "3a446ae61cbf2362d24dc9ab6e72dd2d"
},
"backend/app/__init__.py": {
"mtime": 1789127291.0254955,
"seen": 1789377303.0691833,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/analytics/__init__.py": {
"mtime": 1789128680.8117132,
"seen": 1789377303.0691845,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/analytics/router.py": {
"mtime": 1789129123.9786532,
"seen": 1789377303.0691855,
"ast_hash": "dc4d5f43d1e17ae77b21f774d37c1106",
"semantic_hash": "dc4d5f43d1e17ae77b21f774d37c1106"
},
"backend/app/analytics/service.py": {
"mtime": 1789129314.8210335,
"seen": 1789377303.0691862,
"ast_hash": "0385e76cccd5d140b8cc01af5b74821e",
"semantic_hash": "0385e76cccd5d140b8cc01af5b74821e"
},
"backend/app/audit/__init__.py": {
"mtime": 1789127312.3538313,
"seen": 1789377303.0691874,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/audit/models.py": {
"mtime": 1789127312.353331,
"seen": 1789377303.069188,
"ast_hash": "84a2cb94c3a10150a76f59ff762efc49",
"semantic_hash": "84a2cb94c3a10150a76f59ff762efc49"
},
"backend/app/audit/service.py": {
"mtime": 1789127312.3538313,
"seen": 1789377303.0691888,
"ast_hash": "791163353fbef85a77ace789c704188c",
"semantic_hash": "791163353fbef85a77ace789c704188c"
},
"backend/app/budgeting/__init__.py": {
"mtime": 1789127598.9634895,
"seen": 1789377303.0691895,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/budgeting/models.py": {
"mtime": 1789356526.9709191,
"seen": 1789377303.0691907,
"ast_hash": "039081cab3d91704e8f8a7152a981304",
"semantic_hash": "039081cab3d91704e8f8a7152a981304"
},
"backend/app/budgeting/router.py": {
"mtime": 1789129314.8240335,
"seen": 1789377303.0691917,
"ast_hash": "c352243e4fb85ab49f9cf4532c11bd3f",
"semantic_hash": "c352243e4fb85ab49f9cf4532c11bd3f"
},
"backend/app/budgeting/service.py": {
"mtime": 1789730971.072982,
"seen": 1789735433.158112,
"ast_hash": "c30d4c58608f88b81ff05be63a314247",
"semantic_hash": ""
},
"backend/app/categorization/__init__.py": {
"mtime": 1789127372.62572,
"seen": 1789377303.0691931,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/categorization/models.py": {
"mtime": 1789130236.8679283,
"seen": 1789377303.0691943,
"ast_hash": "922083067aa2a24d67954296844db8f0",
"semantic_hash": "922083067aa2a24d67954296844db8f0"
},
"backend/app/categorization/service.py": {
"mtime": 1789128297.7359908,
"seen": 1789377303.0691953,
"ast_hash": "9590b3c99e7c2b20b40d895d7495b5cf",
"semantic_hash": "9590b3c99e7c2b20b40d895d7495b5cf"
},
"backend/app/config.py": {
"mtime": 1789356583.024539,
"seen": 1789377303.069196,
"ast_hash": "af0c778e49e82763261430b477375048",
"semantic_hash": "af0c778e49e82763261430b477375048"
},
"backend/app/db/__init__.py": {
"mtime": 1789127291.0254955,
"seen": 1789377303.069197,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/db/base.py": {
"mtime": 1789128247.897287,
"seen": 1789377303.069198,
"ast_hash": "ec981c0f87b0470519cca7727172a950",
"semantic_hash": "ec981c0f87b0470519cca7727172a950"
},
"backend/app/db/models.py": {
"mtime": 1789128813.706167,
"seen": 1789377303.0691986,
"ast_hash": "338516ce68785afd5a7fe37f06e1c56f",
"semantic_hash": "338516ce68785afd5a7fe37f06e1c56f"
},
"backend/app/db/session.py": {
"mtime": 1789128374.1340652,
"seen": 1789377303.0691996,
"ast_hash": "9e29a1eebfec6e48e97baf28e506b037",
"semantic_hash": "9e29a1eebfec6e48e97baf28e506b037"
},
"backend/app/dependencies.py": {
"mtime": 1789128452.8041663,
"seen": 1789377303.0692003,
"ast_hash": "20666502da4de510d1cf6939e35e9c20",
"semantic_hash": "20666502da4de510d1cf6939e35e9c20"
},
"backend/app/errors.py": {
"mtime": 1789127291.0254955,
"seen": 1789377303.0692015,
"ast_hash": "368489412799d564f127259ecc0c474c",
"semantic_hash": "368489412799d564f127259ecc0c474c"
},
"backend/app/exports/__init__.py": {
"mtime": 1789128752.0481794,
"seen": 1789377303.0692022,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/exports/models.py": {
"mtime": 1789128752.0481794,
"seen": 1789377303.069203,
"ast_hash": "b76e7bca1889bdf298b5158da2f35642",
"semantic_hash": "b76e7bca1889bdf298b5158da2f35642"
},
"backend/app/exports/router.py": {
"mtime": 1789358158.624152,
"seen": 1789377303.0692039,
"ast_hash": "28cc16dfa8d0b6b588e8f3a7d0a2f110",
"semantic_hash": "28cc16dfa8d0b6b588e8f3a7d0a2f110"
},
"backend/app/exports/service.py": {
"mtime": 1789360140.0563023,
"seen": 1789377303.0692048,
"ast_hash": "a9f5a725bd06779f83eb20beda61881c",
"semantic_hash": "a9f5a725bd06779f83eb20beda61881c"
},
"backend/app/exports/tasks.py": {
"mtime": 1789130443.0178635,
"seen": 1789377303.0692055,
"ast_hash": "de092ecca36d64718378a44d06adb5b6",
"semantic_hash": "de092ecca36d64718378a44d06adb5b6"
},
"backend/app/identity/__init__.py": {
"mtime": 1789127312.3538313,
"seen": 1789377303.0692062,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/identity/mailer.py": {
"mtime": 1789357272.300995,
"seen": 1789377303.069207,
"ast_hash": "1e6fd1b285085e96819214bccde90a34",
"semantic_hash": "1e6fd1b285085e96819214bccde90a34"
},
"backend/app/identity/models.py": {
"mtime": 1789130024.833855,
"seen": 1789377303.069208,
"ast_hash": "f7ad1f0468ad21ac2b86e49ef260f747",
"semantic_hash": "f7ad1f0468ad21ac2b86e49ef260f747"
},
"backend/app/identity/router.py": {
"mtime": 1789356583.0255392,
"seen": 1789377303.0692086,
"ast_hash": "00089d34e9d5d5e965334b4520935bf8",
"semantic_hash": "00089d34e9d5d5e965334b4520935bf8"
},
"backend/app/identity/service.py": {
"mtime": 1789130458.2090783,
"seen": 1789377303.0692096,
"ast_hash": "a2efb5e20098740645da8b58bd95073d",
"semantic_hash": "a2efb5e20098740645da8b58bd95073d"
},
"backend/app/imports/__init__.py": {
"mtime": 1789127900.8512905,
"seen": 1789377303.0692103,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/imports/adapters/__init__.py": {
"mtime": 1789733796.3306727,
"seen": 1789735433.158127,
"ast_hash": "0d3191f98e2602dcb37b96eefcb9d2ea",
"semantic_hash": ""
},
"backend/app/imports/adapters/alfa_xlsx.py": {
"mtime": 1789736201.215202,
"seen": 1789736272.9455209,
"ast_hash": "31eee0cded845f9ddaff98d2127f4354",
"semantic_hash": ""
},
"backend/app/imports/adapters/base.py": {
"mtime": 1789733778.7145572,
"seen": 1789735433.1581285,
"ast_hash": "4f6364ab07cf38ba2eea1ba94678d784",
"semantic_hash": ""
},
"backend/app/imports/adapters/ozon_pdf.py": {
"mtime": 1789733868.836172,
"seen": 1789735433.158129,
"ast_hash": "dfa6dc9ccf2c655e6c2eeaceea0cf06a",
"semantic_hash": ""
},
"backend/app/imports/adapters/rocketbank_pdf.py": {
"mtime": 1789734555.2246265,
"seen": 1789735433.1581297,
"ast_hash": "b4f28ba73c56da5f5eb96eb4f129e098",
"semantic_hash": ""
},
"backend/app/imports/blob.py": {
"mtime": 1789129216.5581613,
"seen": 1789377303.0692153,
"ast_hash": "60d315883175474711dff503c70400a5",
"semantic_hash": "60d315883175474711dff503c70400a5"
},
"backend/app/imports/celery_app.py": {
"mtime": 1789130443.0194046,
"seen": 1789377303.069216,
"ast_hash": "03e8b50c4722e8252a34508df1164985",
"semantic_hash": "03e8b50c4722e8252a34508df1164985"
},
"backend/app/imports/crypto.py": {
"mtime": 1789127900.8612905,
"seen": 1789377303.0692167,
"ast_hash": "c72d7849c6f46e6c9a9136f3ae9dc92f",
"semantic_hash": "c72d7849c6f46e6c9a9136f3ae9dc92f"
},
"backend/app/imports/models.py": {
"mtime": 1789730695.7355492,
"seen": 1789735433.1581326,
"ast_hash": "b99c93656a17603691838c9e7963d9ed",
"semantic_hash": ""
},
"backend/app/imports/readers.py": {
"mtime": 1789128289.6877162,
"seen": 1789377303.0692189,
"ast_hash": "0904afccafd9eb544b4d6846fc6384c8",
"semantic_hash": "0904afccafd9eb544b4d6846fc6384c8"
},
"backend/app/imports/router.py": {
"mtime": 1789986959.5578556,
"seen": 1789987085.811699,
"ast_hash": "c092553fcb48fbd7a19aa4000f24a5b8",
"semantic_hash": ""
},
"backend/app/imports/service.py": {
"mtime": 1789987052.7950857,
"seen": 1789987085.8116996,
"ast_hash": "361eeb454e2614b61194d3d067b117d5",
"semantic_hash": ""
},
"backend/app/imports/tasks.py": {
"mtime": 1789356921.2930734,
"seen": 1789377303.0692213,
"ast_hash": "1b26900740c5c6905601ba65d645857b",
"semantic_hash": "1b26900740c5c6905601ba65d645857b"
},
"backend/app/ledger/__init__.py": {
"mtime": 1789127415.7777057,
"seen": 1789377303.0692222,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/ledger/models.py": {
"mtime": 1789130236.8679283,
"seen": 1789377303.069223,
"ast_hash": "81335e0d9f1f5a71be1626ec218da410",
"semantic_hash": "81335e0d9f1f5a71be1626ec218da410"
},
"backend/app/ledger/router.py": {
"mtime": 1789358018.8352346,
"seen": 1789377303.0692236,
"ast_hash": "809411054fbede0694ed6ee863723fe5",
"semantic_hash": "809411054fbede0694ed6ee863723fe5"
},
"backend/app/ledger/service.py": {
"mtime": 1789358018.8349617,
"seen": 1789377303.0692244,
"ast_hash": "96a715590e5af44c494e5c7c817780a3",
"semantic_hash": "96a715590e5af44c494e5c7c817780a3"
},
"backend/app/main.py": {
"mtime": 1789130118.4222198,
"seen": 1789377303.0692253,
"ast_hash": "b4f205a9d87d905121bda1e4a65c20ea",
"semantic_hash": "b4f205a9d87d905121bda1e4a65c20ea"
},
"backend/app/ratelimit.py": {
"mtime": 1789357059.570077,
"seen": 1789377303.069226,
"ast_hash": "e1732ffc6140108ea33bc70591653dd8",
"semantic_hash": "e1732ffc6140108ea33bc70591653dd8"
},
"backend/app/security.py": {
"mtime": 1789360140.0553021,
"seen": 1789377303.069227,
"ast_hash": "0d73845e1a3da2c128cbb1aacd39506d",
"semantic_hash": "0d73845e1a3da2c128cbb1aacd39506d"
},
"backend/app/spaces/__init__.py": {
"mtime": 1789127346.4407892,
"seen": 1789377303.0692277,
"ast_hash": "d41d8cd98f00b204e9800998ecf8427e",
"semantic_hash": "d41d8cd98f00b204e9800998ecf8427e"
},
"backend/app/spaces/models.py": {
"mtime": 1789128247.897287,
"seen": 1789377303.0692286,
"ast_hash": "7bbb57eb5e95d7b59087f478d7c20412",
"semantic_hash": "7bbb57eb5e95d7b59087f478d7c20412"
},
"backend/app/spaces/router.py": {
"mtime": 1789357010.1013982,
"seen": 1789377303.0692296,
"ast_hash": "073fa39a355b257a832ead3830942d8f",
"semantic_hash": "073fa39a355b257a832ead3830942d8f"
},
"backend/app/spaces/service.py": {
"mtime": 1789129887.1068308,
"seen": 1789377303.0692303,
"ast_hash": "99f21c94dad6403b77f4cafed97c1c2e",
"semantic_hash": "99f21c94dad6403b77f4cafed97c1c2e"
},
"backend/app/worker.py": {
"mtime": 1789129206.4285526,
"seen": 1789377303.069231,
"ast_hash": "4788addc575411056226ee892d1e2806",
"semantic_hash": "4788addc575411056226ee892d1e2806"
},
"backend/pyproject.toml": {
"mtime": 1789731892.783645,
"seen": 1789735433.1581438,
"ast_hash": "6ae54b23d066a4a48b7edbbe964af75f",
"semantic_hash": ""
},
"backend/tests/conftest.py": {
"mtime": 1789129509.9246948,
"seen": 1789377303.069233,
"ast_hash": "f74b424930fc6a978616e2e1bd081a0b",
"semantic_hash": "f74b424930fc6a978616e2e1bd081a0b"
},
"backend/tests/test_api.py": {
"mtime": 1789130118.4241138,
"seen": 1789377303.069234,
"ast_hash": "bfec0778131788a91a0bc5c9ec2ad8ba",
"semantic_hash": "bfec0778131788a91a0bc5c9ec2ad8ba"
},
"backend/tests/test_budget.py": {
"mtime": 1789358107.2584474,
"seen": 1789377303.0692346,
"ast_hash": "f6e5c4d1e1d0f2b1941acf8ec9f6678b",
"semantic_hash": "f6e5c4d1e1d0f2b1941acf8ec9f6678b"
},
"backend/tests/test_imports.py": {
"mtime": 1789986980.8700209,
"seen": 1789987085.8117144,
"ast_hash": "8c6aec11825fe46740ad64b658da2a15",
"semantic_hash": ""
},
"backend/tests/test_ledger_properties.py": {
"mtime": 1789129573.0755556,
"seen": 1789377303.0692365,
"ast_hash": "fb7d5e4503c9482b5014e28528f75e2a",
"semantic_hash": "fb7d5e4503c9482b5014e28528f75e2a"
},
"backend/tests/test_postgres_and_s3.py": {
"mtime": 1789734220.6980176,
"seen": 1789735433.1581545,
"ast_hash": "1def023e0a52eed53bcb97557919484b",
"semantic_hash": ""
},
"frontend/eslint.config.js": {
"mtime": 1789128706.6116898,
"seen": 1789377303.069238,
"ast_hash": "ed54799a921d10394f826893acb46876",
"semantic_hash": "ed54799a921d10394f826893acb46876"
},
"frontend/package.json": {
"mtime": 1789378006.2922633,
"seen": 1789735433.1581702,
"ast_hash": "10697e744e175f761f5ddb85c7925350",
"semantic_hash": ""
},
"frontend/scripts/generate-api.mjs": {
"mtime": 1789126898.3307452,
"seen": 1789377303.0692399,
"ast_hash": "61461f226e7d2bba9730be8b7da165f9",
"semantic_hash": "61461f226e7d2bba9730be8b7da165f9"
},
"frontend/src/App.tsx": {
"mtime": 1789127518.1600943,
"seen": 1789377303.0692406,
"ast_hash": "752eea473bb3bb6d8a2dd28f5ae87280",
"semantic_hash": "752eea473bb3bb6d8a2dd28f5ae87280"
},
"frontend/src/api/client.ts": {
"mtime": 1789127044.43022,
"seen": 1789377303.0692413,
"ast_hash": "dbea075a006b7703340f7f93e69d53ac",
"semantic_hash": "dbea075a006b7703340f7f93e69d53ac"
},
"frontend/src/api/endpoints.ts": {
"mtime": 1789357272.302138,
"seen": 1789377303.069242,
"ast_hash": "c4d7620bf50bef28b20286b6093e7e30",
"semantic_hash": "c4d7620bf50bef28b20286b6093e7e30"
},
"frontend/src/api/generated/schema.d.ts": {
"mtime": 1789130160.8338337,
"seen": 1789377303.069243,
"ast_hash": "8857d50fb909f019e1d8c03009c0ada7",
"semantic_hash": "8857d50fb909f019e1d8c03009c0ada7"
},
"frontend/src/api/queryKeys.ts": {
"mtime": 1789127044.43022,
"seen": 1789377303.0692437,
"ast_hash": "5ec23457cf6b0f223df671d3bc0f8455",
"semantic_hash": "5ec23457cf6b0f223df671d3bc0f8455"
},
"frontend/src/api/types.ts": {
"mtime": 1789357272.302138,
"seen": 1789377303.069248,
"ast_hash": "0d3865b4525321cf5a6b360f72de5f5a",
"semantic_hash": "0d3865b4525321cf5a6b360f72de5f5a"
},
"frontend/src/app/router.tsx": {
"mtime": 1789357305.2954476,
"seen": 1789377303.0692487,
"ast_hash": "0366437498b1933d212cd103252a0b11",
"semantic_hash": "0366437498b1933d212cd103252a0b11"
},
"frontend/src/components/charts/ColumnChart.tsx": {
"mtime": 1789127463.8093653,
"seen": 1789377303.0692496,
"ast_hash": "3f2aeaca948431b2fd4cd86bedaa9ede",
"semantic_hash": "3f2aeaca948431b2fd4cd86bedaa9ede"
},
"frontend/src/components/charts/HorizontalBars.tsx": {
"mtime": 1789127423.3778224,
"seen": 1789377303.06925,
"ast_hash": "e341240d131f12fe680424010279ddd3",
"semantic_hash": "e341240d131f12fe680424010279ddd3"
},
"frontend/src/components/charts/PlanFactBars.tsx": {
"mtime": 1789127423.3794281,
"seen": 1789377303.069251,
"ast_hash": "f581486baa48447ec4c573acef3190ef",
"semantic_hash": "f581486baa48447ec4c573acef3190ef"
},
"frontend/src/components/layout/AppLayout.tsx": {
"mtime": 1789127423.3794281,
"seen": 1789377303.0692518,
"ast_hash": "ae7a94355ca990e4efc91664db0d86c2",
"semantic_hash": "ae7a94355ca990e4efc91664db0d86c2"
},
"frontend/src/components/layout/LangSwitcher.tsx": {
"mtime": 1789127423.3794281,
"seen": 1789377303.069253,
"ast_hash": "5be78b1684339d7ea82677fe1828162c",
"semantic_hash": "5be78b1684339d7ea82677fe1828162c"
},
"frontend/src/components/ui/Badge.tsx": {
"mtime": 1789127112.4896402,
"seen": 1789377303.0692537,
"ast_hash": "f1a5dd94fafcaa4ab6edeb2221ffdd18",
"semantic_hash": "f1a5dd94fafcaa4ab6edeb2221ffdd18"
},
"frontend/src/components/ui/Button.tsx": {
"mtime": 1789127112.4896402,
"seen": 1789377303.0692544,
"ast_hash": "ec61aded7bd1c0bc626b9f85243e6d24",
"semantic_hash": "ec61aded7bd1c0bc626b9f85243e6d24"
},
"frontend/src/components/ui/ConfirmDialog.tsx": {
"mtime": 1789127142.0840394,
"seen": 1789377303.0692554,
"ast_hash": "45b3ad6925a6e69045a59593882c7ffc",
"semantic_hash": "45b3ad6925a6e69045a59593882c7ffc"
},
"frontend/src/components/ui/EmptyState.tsx": {
"mtime": 1789127142.0700393,
"seen": 1789377303.0692563,
"ast_hash": "853f2c5b079b2cfa9e4f12363cefba3a",
"semantic_hash": "853f2c5b079b2cfa9e4f12363cefba3a"
},
"frontend/src/components/ui/ErrorState.tsx": {
"mtime": 1789127142.067039,
"seen": 1789377303.069257,
"ast_hash": "6827eb04b914e8c675e842fea74b5ba5",
"semantic_hash": "6827eb04b914e8c675e842fea74b5ba5"
},
"frontend/src/components/ui/Modal.tsx": {
"mtime": 1789127142.054039,
"seen": 1789377303.069259,
"ast_hash": "41b09f3998ca6f6d7ad415cceff4b9ea",
"semantic_hash": "41b09f3998ca6f6d7ad415cceff4b9ea"
},
"frontend/src/components/ui/MonthSwitcher.tsx": {
"mtime": 1789127142.0860393,
"seen": 1789377303.06926,
"ast_hash": "ddb486640fa05b742fa71167949a2aca",
"semantic_hash": "ddb486640fa05b742fa71167949a2aca"
},
"frontend/src/components/ui/PageHeader.tsx": {
"mtime": 1789127142.0810394,
"seen": 1789377303.0692606,
"ast_hash": "332c481c9030b26221b2b080482973a2",
"semantic_hash": "332c481c9030b26221b2b080482973a2"
},
"frontend/src/components/ui/QueryGate.tsx": {
"mtime": 1789128567.5977647,
"seen": 1789377303.0692613,
"ast_hash": "ee535a82d89a1d2bc30c5ec4907817c1",
"semantic_hash": "ee535a82d89a1d2bc30c5ec4907817c1"
},
"frontend/src/components/ui/Spinner.tsx": {
"mtime": 1789127142.061039,
"seen": 1789377303.0692623,
"ast_hash": "40c69d38fa5a96969026c1486ac981a5",
"semantic_hash": "40c69d38fa5a96969026c1486ac981a5"
},
"frontend/src/components/ui/Toaster.tsx": {
"mtime": 1789127142.0780392,
"seen": 1789377303.0692632,
"ast_hash": "d76eafaf1a72d8435cbce2fd9d2f8764",
"semantic_hash": "d76eafaf1a72d8435cbce2fd9d2f8764"
},
"frontend/src/components/ui/fields.tsx": {
"mtime": 1789128567.5907645,
"seen": 1789377303.069264,
"ast_hash": "5e1b157a5cd5b484582ba959049443aa",
"semantic_hash": "5e1b157a5cd5b484582ba959049443aa"
},
"frontend/src/hooks/useAccounts.ts": {
"mtime": 1789127463.8123653,
"seen": 1789377303.0692647,
"ast_hash": "5cbd401bea752132870ca35a0b2cf29f",
"semantic_hash": "5cbd401bea752132870ca35a0b2cf29f"
},
"frontend/src/hooks/useAnalytics.ts": {
"mtime": 1789127476.179531,
"seen": 1789377303.0692656,
"ast_hash": "2a0cad57cdfc7456c967b3fd122e6017",
"semantic_hash": "2a0cad57cdfc7456c967b3fd122e6017"
},
"frontend/src/hooks/useAuth.ts": {
"mtime": 1789357272.302138,
"seen": 1789377303.0692663,
"ast_hash": "40f9f47b108ec0b484f9564bd1d319cb",
"semantic_hash": "40f9f47b108ec0b484f9564bd1d319cb"
},
"frontend/src/hooks/useBudget.ts": {
"mtime": 1789127463.8123653,
"seen": 1789377303.0692673,
"ast_hash": "06c203b89322dca6cf80931f3558b0dc",
"semantic_hash": "06c203b89322dca6cf80931f3558b0dc"
},
"frontend/src/hooks/useImports.ts": {
"mtime": 1789127476.176531,
"seen": 1789377303.0692682,
"ast_hash": "d23f116da79b15438601751b45519268",
"semantic_hash": "d23f116da79b15438601751b45519268"
},
"frontend/src/hooks/useOperations.ts": {
"mtime": 1789127463.8123653,
"seen": 1789377303.069269,
"ast_hash": "3cac9924554a5465276a2c4431121866",
"semantic_hash": "3cac9924554a5465276a2c4431121866"
},
"frontend/src/hooks/useSpaceLifecycle.ts": {
"mtime": 1789356647.3201435,
"seen": 1789377303.0692697,
"ast_hash": "87c19951711d1798821f227eac36d609",
"semantic_hash": "87c19951711d1798821f227eac36d609"
},
"frontend/src/i18n/index.ts": {
"mtime": 1789128261.8073747,
"seen": 1789377303.0692706,
"ast_hash": "243512f6e095deeaa29393b7c831777f",
"semantic_hash": "243512f6e095deeaa29393b7c831777f"
},
"frontend/src/i18n/locales/en.json": {
"mtime": 1789357272.302995,
"seen": 1789377303.0692713,
"ast_hash": "2ce7444db1936d9c5fcca10b901c4afc",
"semantic_hash": "2ce7444db1936d9c5fcca10b901c4afc"
},
"frontend/src/i18n/locales/ru.json": {
"mtime": 1789357272.302995,
"seen": 1789377303.0692723,
"ast_hash": "ae00c080e0b9a10b6004c4094bcdb1ea",
"semantic_hash": "ae00c080e0b9a10b6004c4094bcdb1ea"
},
"frontend/src/main.tsx": {
"mtime": 1789127518.1660943,
"seen": 1789377303.069273,
"ast_hash": "9219312484aae5efecb6534357f9aadf",
"semantic_hash": "9219312484aae5efecb6534357f9aadf"
},
"frontend/src/pages/AcceptInvitePage.tsx": {
"mtime": 1789357272.302138,
"seen": 1789377303.069274,
"ast_hash": "11475c9e3ab1b5f50cbd8e5e63cca4cb",
"semantic_hash": "11475c9e3ab1b5f50cbd8e5e63cca4cb"
},
"frontend/src/pages/AccountsPage.tsx": {
"mtime": 1789127567.9147615,
"seen": 1789377303.0692747,
"ast_hash": "5da5d66e5eb40222e4ceb1f75ca951bd",
"semantic_hash": "5da5d66e5eb40222e4ceb1f75ca951bd"
},
"frontend/src/pages/AnalyticsPage.tsx": {
"mtime": 1789127904.7983484,
"seen": 1789377303.0692756,
"ast_hash": "4a83070566fde66ff6423b1064cde4e8",
"semantic_hash": "4a83070566fde66ff6423b1064cde4e8"
},
"frontend/src/pages/BudgetPage.tsx": {
"mtime": 1789127621.1764753,
"seen": 1789377303.0692763,
"ast_hash": "c1421d4088b0f174dc7ecb082b2a257d",
"semantic_hash": "c1421d4088b0f174dc7ecb082b2a257d"
},
"frontend/src/pages/ImportDetailPage.tsx": {
"mtime": 1789127866.2017796,
"seen": 1789377303.0692773,
"ast_hash": "52adf2e149d39f2b2d77f96640c72b5e",
"semantic_hash": "52adf2e149d39f2b2d77f96640c72b5e"
},
"frontend/src/pages/ImportsPage.tsx": {
"mtime": 1789127830.2842758,
"seen": 1789377303.069278,
"ast_hash": "721376253dda864ff81a5025691947e0",
"semantic_hash": "721376253dda864ff81a5025691947e0"
},
"frontend/src/pages/LoginPage.tsx": {
"mtime": 1789357272.302138,
"seen": 1789377303.069279,
"ast_hash": "6cb36d7a770d36c3dddf1dbb90ebc545",
"semantic_hash": "6cb36d7a770d36c3dddf1dbb90ebc545"
},
"frontend/src/pages/NotFoundPage.tsx": {
"mtime": 1789127518.2310953,
"seen": 1789377303.0692797,
"ast_hash": "53e014a77ff3070820a83458af08a5fd",
"semantic_hash": "53e014a77ff3070820a83458af08a5fd"
},
"frontend/src/pages/OperationsPage.tsx": {
"mtime": 1789128734.6790774,
"seen": 1789377303.0692806,
"ast_hash": "f4ae42adff88554aef7c522ce79daca3",
"semantic_hash": "f4ae42adff88554aef7c522ce79daca3"
},
"frontend/src/pages/ResetPasswordPage.tsx": {
"mtime": 1789357305.2944477,
"seen": 1789377303.0692816,
"ast_hash": "4ecaf25ff754290936fd87db84f19414",
"semantic_hash": "4ecaf25ff754290936fd87db84f19414"
},
"frontend/src/pages/ReviewQueuePage.tsx": {
"mtime": 1789127802.1288989,
"seen": 1789377303.0692823,
"ast_hash": "b3f57d73ef457c9b2427d74603972213",
"semantic_hash": "b3f57d73ef457c9b2427d74603972213"
},
"frontend/src/pages/SettingsPage.tsx": {
"mtime": 1789128572.7868366,
"seen": 1789377303.069283,
"ast_hash": "6a35c2799ba84fdbc1367d768907e8fa",
"semantic_hash": "6a35c2799ba84fdbc1367d768907e8fa"
},
"frontend/src/test/accounts-page.test.tsx": {
"mtime": 1789128626.8475864,
"seen": 1789377303.0692837,
"ast_hash": "d17270fb5ca22286d3d62d7e2679fbcb",
"semantic_hash": "d17270fb5ca22286d3d62d7e2679fbcb"
},
"frontend/src/test/budget-plan.test.tsx": {
"mtime": 1789128448.8001103,
"seen": 1789377303.0692847,
"ast_hash": "b2abf4ff7b3ea5b6f730cddd021b4127",
"semantic_hash": "b2abf4ff7b3ea5b6f730cddd021b4127"
},
"frontend/src/test/chart-accessible-table.test.tsx": {
"mtime": 1789128448.8031104,
"seen": 1789377303.0692854,
"ast_hash": "e8f2187db8969b2fb4975775e3dbdcb9",
"semantic_hash": "e8f2187db8969b2fb4975775e3dbdcb9"
},
"frontend/src/test/helpers.tsx": {
"mtime": 1789128456.9108279,
"seen": 1789377303.0692863,
"ast_hash": "041d8de84ce39044e32d9a4e45f176a0",
"semantic_hash": "041d8de84ce39044e32d9a4e45f176a0"
},
"frontend/src/test/i18n-parity.test.ts": {
"mtime": 1789128572.7505064,
"seen": 1789377303.069287,
"ast_hash": "cc870c8a667b6982c9fabd654468a46f",
"semantic_hash": "cc870c8a667b6982c9fabd654468a46f"
},
"frontend/src/test/import-polling.test.tsx": {
"mtime": 1789128692.423248,
"seen": 1789377303.0692883,
"ast_hash": "2b83dee550ca31b0d5980c8ce3235fd0",
"semantic_hash": "2b83dee550ca31b0d5980c8ce3235fd0"
},
"frontend/src/test/login-page.test.tsx": {
"mtime": 1789128286.0698264,
"seen": 1789377303.069289,
"ast_hash": "52ebeeb4fb554b4c830113cebcf9f530",
"semantic_hash": "52ebeeb4fb554b4c830113cebcf9f530"
},
"frontend/src/test/money.test.ts": {
"mtime": 1789128261.8154838,
"seen": 1789377303.0692897,
"ast_hash": "8faf4d33972c1583baa98fcd8160dbd0",
"semantic_hash": "8faf4d33972c1583baa98fcd8160dbd0"
},
"frontend/src/test/query-keys.test.ts": {
"mtime": 1789128626.8519824,
"seen": 1789377303.0692904,
"ast_hash": "554aa60181736710caf28df6f9b62a66",
"semantic_hash": "554aa60181736710caf28df6f9b62a66"
},
"frontend/src/test/setup.ts": {
"mtime": 1789128261.8154838,
"seen": 1789377303.0692916,
"ast_hash": "5b0ef0fbf28fe880eac77ca470cdb72f",
"semantic_hash": "5b0ef0fbf28fe880eac77ca470cdb72f"
},
"frontend/src/vite-env.d.ts": {
"mtime": 1789127028.213317,
"seen": 1789377303.0692923,
"ast_hash": "0352474ba2918efe13895edbc3780d94",
"semantic_hash": "0352474ba2918efe13895edbc3780d94"
},
"frontend/tsconfig.json": {
"mtime": 1789126877.2434592,
"seen": 1789377303.069293,
"ast_hash": "c57160ea7b6323de41adceed69142721",
"semantic_hash": "c57160ea7b6323de41adceed69142721"
},
"frontend/vite.config.ts": {
"mtime": 1789126877.239459,
"seen": 1789377303.0692937,
"ast_hash": "b99eeb04d213e3680abe9c3854521e80",
"semantic_hash": "b99eeb04d213e3680abe9c3854521e80"
},
"infra/postgres/init-app-role.sh": {
"mtime": 1789129887.1068308,
"seen": 1789377303.069295,
"ast_hash": "4c2b103cde06bc6df773f005c00b73d6",
"semantic_hash": "4c2b103cde06bc6df773f005c00b73d6"
},
".kilo/plans/1789034192214-financial-tracker-mvp.md": {
"mtime": 1789099088.960013,
"seen": 1789377303.0692966,
"ast_hash": "a2f21668b5ef4c0005316ef3e54b6a28",
"semantic_hash": "a2f21668b5ef4c0005316ef3e54b6a28"
},
"CONTEXT.md": {
"mtime": 1789126288.30262,
"seen": 1789377303.0692973,
"ast_hash": "c8211b75ee8dfba83e37f06ff219d2b1",
"semantic_hash": "c8211b75ee8dfba83e37f06ff219d2b1"
},
"README.md": {
"mtime": 1789358273.3585796,
"seen": 1789377303.0692983,
"ast_hash": "79eb6002a05286e6655e6fe3904f5b29",
"semantic_hash": "79eb6002a05286e6655e6fe3904f5b29"
},
"docs/adr/0001-financial-space-ownership-and-rls.md": {
"mtime": 1789126779.7591345,
"seen": 1789377303.0692992,
"ast_hash": "8fbddf58406caccd2d5a5b0e5f9ffab4",
"semantic_hash": "8fbddf58406caccd2d5a5b0e5f9ffab4"
},
"docs/adr/0002-journal-and-budget-reserves.md": {
"mtime": 1789126779.7821348,
"seen": 1789377303.0693,
"ast_hash": "6ca9f9b8f91733ce37e15f339a656726",
"semantic_hash": "6ca9f9b8f91733ce37e15f339a656726"
},
"docs/adr/0003-versioned-statement-imports.md": {
"mtime": 1789732137.8203447,
"seen": 1789735433.1582236,
"ast_hash": "e05b45d637e097a3898bc1b2f2160fe7",
"semantic_hash": ""
},
"docs/adr/0004-provider-neutral-encrypted-data-boundary.md": {
"mtime": 1789126790.5422814,
"seen": 1789377303.0693016,
"ast_hash": "460e37c748836b116a4ea820768a8007",
"semantic_hash": "460e37c748836b116a4ea820768a8007"
},
"docs/operations/backup-and-restore.md": {
"mtime": 1789126817.735651,
"seen": 1789377303.0693026,
"ast_hash": "188110b691922b9a23f4efa2ef4627e2",
"semantic_hash": "188110b691922b9a23f4efa2ef4627e2"
},
"docs/operations/beta-release-runbook.md": {
"mtime": 1789126842.5229878,
"seen": 1789377303.0693033,
"ast_hash": "00dada41b2d3425da89bcd277acce41e",
"semantic_hash": "00dada41b2d3425da89bcd277acce41e"
},
"docs/operations/deletion-ledger.md": {
"mtime": 1789126842.4969876,
"seen": 1789377303.0693042,
"ast_hash": "1aff4ccf6a2dbcf86ed72fd34bb69c6f",
"semantic_hash": "1aff4ccf6a2dbcf86ed72fd34bb69c6f"
},
"docs/parsers/private-fixture-gate.md": {
"mtime": 1789734164.3005245,
"seen": 1789735433.1582274,
"ast_hash": "02086c7b911cf06d3d170ac04be7592e",
"semantic_hash": ""
},
"frontend/README.md": {
"mtime": 1789128756.9573848,
"seen": 1789377303.069306,
"ast_hash": "3c43f67a81ce2c7b0cd18aeab85ed2c2",
"semantic_hash": "3c43f67a81ce2c7b0cd18aeab85ed2c2"
},
"frontend/index.html": {
"mtime": 1789126886.1155794,
"seen": 1789377303.0693069,
"ast_hash": "42788af442bf05e487937f435141f84b",
"semantic_hash": "42788af442bf05e487937f435141f84b"
},
"infra/README.md": {
"mtime": 1789357215.8087454,
"seen": 1789377303.0693076,
"ast_hash": "0a7bb82cce04631f36f2d561a1665478",
"semantic_hash": "0a7bb82cce04631f36f2d561a1665478"
},
"infra/docker-compose.yml": {
"mtime": 1789400438.2433548,
"seen": 1789735433.1582313,
"ast_hash": "9f04dd1906326ddea306584fe68a2ae4",
"semantic_hash": ""
},
"frontend/public/favicon.svg": {
"mtime": 1789126886.1235795,
"seen": 1789377303.0693095,
"ast_hash": "83637b50c3736f9f0675b7f23ff5a51e",
"semantic_hash": "83637b50c3736f9f0675b7f23ff5a51e"
},
".kilo/kilo.json": {
"mtime": 1789377478.0622306,
"seen": 1789735433.1580946,
"ast_hash": "1839ad09cc2e72bfeb650219265f2e20",
"semantic_hash": ""
},
"backend/alembic/versions/20260918_0002_hold_lifecycle.py": {
"mtime": 1789735291.8675542,
"seen": 1789735433.1581025,
"ast_hash": "2a669c07ecd8d473e703bc823cb26183",
"semantic_hash": ""
},
"backend/tests/test_adapter_contracts.py": {
"mtime": 1789735944.366427,
"seen": 1789736272.9455402,
"ast_hash": "c3f63850c85b7c5262e66370ab7ab8a9",
"semantic_hash": ""
},
"backend/tests/test_import_holds.py": {
"mtime": 1789736201.2172022,
"seen": 1789736272.9455423,
"ast_hash": "6f14226a83e65efbaaee6446d4ec4cb6",
"semantic_hash": ""
},
"backend/tests/test_private_fixtures.py": {
"mtime": 1789737822.4871244,
"seen": 1789737916.7915628,
"ast_hash": "f14cd118f1e8d6b5088536d50d2b965a",
"semantic_hash": ""
},
".gitea/workflows/ci.yml": {
"mtime": 1789736949.8123386,
"seen": 1789737916.791613,
"ast_hash": "f0891b841c5e24c761f5067a4ea23637",
"semantic_hash": ""
},
".kilo/plans/1789727434954-gitea-ci-real-statement-imports.md": {
"mtime": 1789729895.9594803,
"seen": 1789735433.1582198,
"ast_hash": "eb38e75994c5fc29d4137adb7e0e37f8",
"semantic_hash": ""
},
"frontend/pnpm-workspace.yaml": {
"mtime": 1789377936.9976747,
"seen": 1789735433.1582298,
"ast_hash": "2ae2370fa527b015cd7bd98dc7ca2a6b",
"semantic_hash": ""
},
".kilo/plugins/graphify.js": {
"mtime": 1789967188.4955833,
"seen": 1789987085.8116593,
"ast_hash": "af79b534b1739d62b450e39c9b50e362",
"semantic_hash": ""
},
"AGENTS.md": {
"mtime": 1789967188.495481,
"seen": 1789987085.8117683,
"ast_hash": "6a47d64fcc136324ca395065a707ef76",
"semantic_hash": ""
}
}
+112 -109
View File
@@ -1,51 +1,51 @@
# Graph Report - finance (2026-09-18) # Graph Report - finance (2026-09-21)
## Corpus Check ## Corpus Check
- 149 files · ~63,842 words - 151 files · ~64,809 words
- Verdict: corpus is large enough that graph structure adds value. - Verdict: corpus is large enough that graph structure adds value.
## Summary ## Summary
- 1267 nodes · 3697 edges · 75 communities (54 shown, 15 thin omitted) - 1277 nodes · 3734 edges · 79 communities (54 shown, 18 thin omitted)
- Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 70 edges (avg confidence: 0.93) - Extraction: 98% EXTRACTED · 2% INFERRED · 0% AMBIGUOUS · INFERRED: 71 edges (avg confidence: 0.93)
- Token cost: 0 input · 0 output - Token cost: 0 input · 0 output
## Graph Freshness ## Graph Freshness
- Built from commit: `fb91da79` - Built from commit: `35b9ea83`
- Run `git rev-parse HEAD` and compare to check if the graph is stale. - Run `git rev-parse HEAD` and compare to check if the graph is stale.
- Run `graphify update .` after code changes (no API cost). - Run `graphify update .` after code changes (no API cost).
## Community Hubs (Navigation) ## Community Hubs (Navigation)
- identity/router.py - identity/service.py
- budgeting/router.py - budgeting/router.py
- Base - Base
- set_space_context - set_space_context
- imports/router.py - imports/router.py
- alfa_xlsx.py - alfa_xlsx.py
- Session - ledger/service.py
- types.ts - types.ts
- router.tsx - router.tsx
- ledger/router.py - ledger/router.py
- AccountsPage.tsx - main.py
- compilerOptions - compilerOptions
- AnalyticsPage.tsx - AnalyticsPage.tsx
- analytics/router.py - exports/router.py
- devDependencies - devDependencies
- test_import_holds.py - Session
- dependencies - dependencies
- ImportDetailPage.tsx - ImportDetailPage.tsx
- OperationsPage.tsx - OperationsPage.tsx
- SettingsPage.tsx - useOperations.ts
- Toaster.tsx - Toaster.tsx
- ozon_pdf.py - adapters/base.py
- BudgetPage.tsx - BudgetPage.tsx
- client.ts - client.ts
- fields.tsx - AccountsPage.tsx
- Docker Compose Development Stack - Docker Compose Development Stack
- Financial Space - Financial Space
- scripts - scripts
- dependencies.py - exports/service.py
- General Pool - General Pool
- TestBaseContract - test_adapter_contracts.py
- Per-File Envelope Encryption - Per-File Envelope Encryption
- Operation - Operation
- Versioned Statement Imports - Versioned Statement Imports
@@ -53,7 +53,7 @@
- Closed Beta Launch Gates - Closed Beta Launch Gates
- package.json - package.json
- Financial Tracker MVP - Financial Tracker MVP
- test_adapter_contracts.py - TestRocketbankDebitContract
- generate-api.mjs - generate-api.mjs
- schema.d.ts - schema.d.ts
- test_api.py - test_api.py
@@ -65,14 +65,14 @@
- graphify - graphify
- @types/react-dom - @types/react-dom
- typescript-eslint - typescript-eslint
- SourceRow - OzonDebitPdfAdapter
- vitest - vitest
- Favicon SVG (ledger lines icon) - Favicon SVG (ledger lines icon)
- init-app-role.sh - init-app-role.sh
- Payee - Payee
- finance-backend - finance-backend
- LocalDirectoryBlobStore - LocalDirectoryBlobStore
- account_fingerprint - TestOzonDebitContract
- Gitea CI and real statement imports - Gitea CI and real statement imports
- 20260918_0002_hold_lifecycle.py - 20260918_0002_hold_lifecycle.py
- eslint-plugin-react-hooks - eslint-plugin-react-hooks
@@ -80,22 +80,25 @@
- @testing-library/dom - @testing-library/dom
- @testing-library/react - @testing-library/react
- vite - vite
- spaces/router.py
- identity/service.py
- get_settings
- ColumnChart.tsx - ColumnChart.tsx
- conftest.py
- imports/service.py
- BlobStore
- AppError
- celery_app.py
- AGENTS.md
## God Nodes (most connected - your core abstractions) ## God Nodes (most connected - your core abstractions)
1. `Session` - 140 edges 1. `Session` - 146 edges
2. `AppError` - 53 edges 2. `AppError` - 54 edges
3. `Base` - 47 edges 3. `Base` - 47 edges
4. `PkMixin` - 39 edges 4. `PkMixin` - 39 edges
5. `get_settings()` - 38 edges 5. `get_settings()` - 38 edges
6. `TimestampMixin` - 38 edges 6. `TimestampMixin` - 38 edges
7. `conflict()` - 35 edges 7. `conflict()` - 35 edges
8. `record()` - 34 edges 8. `record()` - 34 edges
9. `UnsupportedStatementLayout` - 30 edges 9. `StatementDocument` - 32 edges
10. `bad_request()` - 29 edges 10. `UnsupportedStatementLayout` - 30 edges
## Surprising Connections (you probably didn't know these) ## Surprising Connections (you probably didn't know these)
- `Typed Same-Origin API Client` --conceptually_related_to--> `Same-Origin Reverse Proxy` [INFERRED] - `Typed Same-Origin API Client` --conceptually_related_to--> `Same-Origin Reverse Proxy` [INFERRED]
@@ -117,107 +120,107 @@
- **Financial Space Tenancy Boundary** — context_financial_space, context_membership, docs_adr_0001_financial_space_ownership_and_rls_defense_in_depth_authorization, docs_adr_0001_financial_space_ownership_and_rls_transaction_local_rls, docs_adr_0001_financial_space_ownership_and_rls_cross_space_integrity [EXTRACTED 1.00] - **Financial Space Tenancy Boundary** — context_financial_space, context_membership, docs_adr_0001_financial_space_ownership_and_rls_defense_in_depth_authorization, docs_adr_0001_financial_space_ownership_and_rls_transaction_local_rls, docs_adr_0001_financial_space_ownership_and_rls_cross_space_integrity [EXTRACTED 1.00]
- **Safe Statement Import Flow** — context_statement_import, context_parse_run, context_source_record, docs_adr_0003_versioned_statement_imports_immutable_statement_document, docs_adr_0003_versioned_statement_imports_conservative_deduplication, docs_adr_0003_versioned_statement_imports_atomic_confirm_revert [EXTRACTED 1.00] - **Safe Statement Import Flow** — context_statement_import, context_parse_run, context_source_record, docs_adr_0003_versioned_statement_imports_immutable_statement_document, docs_adr_0003_versioned_statement_imports_conservative_deduplication, docs_adr_0003_versioned_statement_imports_atomic_confirm_revert [EXTRACTED 1.00]
## Communities (75 total, 15 thin omitted) ## Communities (79 total, 18 thin omitted)
### Community 0 - "identity/router.py" ### Community 0 - "identity/service.py"
Cohesion: 0.20 Cohesion: 0.06
Nodes (22): accept_invite(), AcceptInviteBody, confirm_password_reset(), create_invite(), InviteBody, login(), LoginBody, logout() (+14 more) Nodes (83): current_context(), current_session(), alias, Context, CurrentSession, Db, Header, User (+75 more)
### Community 1 - "budgeting/router.py" ### Community 1 - "budgeting/router.py"
Cohesion: 0.20 Cohesion: 0.14
Nodes (34): AssignmentItem, assignments(), AssignmentsBody, categories(), CategoryBody, CategoryFundingBody, close(), close_frontend() (+26 more) Nodes (43): balances(), cash_flow(), category_spend(), plan_fact(), Context, date, Db, get (+35 more)
### Community 2 - "Base" ### Community 2 - "Base"
Cohesion: 0.06 Cohesion: 0.07
Nodes (87): downgrade(), upgrade(), AuditEvent, Append-only domain/security event. Never stores raw financial payloads., ExpectedIncome, ExpectedIncomeMatch, FundingMovement, PeriodClosure (+79 more) Nodes (76): downgrade(), upgrade(), AuditEvent, Append-only domain/security event. Never stores raw financial payloads., ExpectedIncome, ExpectedIncomeMatch, FundingMovement, PeriodClosure (+68 more)
### Community 3 - "set_space_context" ### Community 3 - "set_space_context"
Cohesion: 0.07 Cohesion: 0.22
Nodes (43): get_db(), get_engine(), get_session_factory(), Any, sessionmaker, Transaction-local RLS context. No-op on SQLite (tests); fail-closed on…, Transactional scope for workers/endpoints that own the transaction lifecycle., session_scope() (+35 more) Nodes (14): get_db(), get_session_factory(), Any, sessionmaker, Transaction-local RLS context. No-op on SQLite (tests); fail-closed on…, Transactional scope for workers/endpoints that own the transaction lifecycle., session_scope(), set_space_context() (+6 more)
### Community 4 - "imports/router.py" ### Community 4 - "imports/router.py"
Cohesion: 0.15 Cohesion: 0.16
Nodes (33): confirm(), ConfirmBody, decide_row(), DecisionBody, delete_source(), detail(), HoldResolveBody, imports() (+25 more) Nodes (33): confirm(), ConfirmBody, decide_row(), DecisionBody, delete_source(), detail(), HoldResolveBody, imports() (+25 more)
### Community 5 - "alfa_xlsx.py" ### Community 5 - "alfa_xlsx.py"
Cohesion: 0.14 Cohesion: 0.17
Nodes (29): AlfaCreditXlsxAdapter, AlfaDebitXlsxAdapter, _AlfaHeaderMeta, AlfaXlsxAdapterBase, _cell_str(), _debit_find_header(), _debit_header_metadata(), _debit_header_value() (+21 more) Nodes (24): AlfaDebitXlsxAdapter, _AlfaHeaderMeta, AlfaXlsxAdapterBase, _cell_str(), _debit_find_header(), _debit_header_metadata(), _debit_header_value(), _debit_norm() (+16 more)
### Community 6 - "Session" ### Community 6 - "ledger/service.py"
Cohesion: 0.07 Cohesion: 0.11
Nodes (112): balances(), plan_fact(), Any, record(), BudgetPeriod, Calendar month budget period in the space timezone (month granularity)., add_expected_income(), archive_category() (+104 more) Nodes (62): Any, record(), get_income_source(), bad_request(), conflict(), not_found(), Any, _apply_hold_matches() (+54 more)
### Community 7 - "types.ts" ### Community 7 - "types.ts"
Cohesion: 0.05 Cohesion: 0.05
Nodes (53): api, AcceptInvitePayload, Account, AccountStatus, BalanceItem, BalancesReport, BudgetBlocker, BudgetCategory (+45 more) Nodes (52): api, AcceptInvitePayload, Account, AccountStatus, BalanceItem, BalancesReport, BudgetBlocker, BudgetCategory (+44 more)
### Community 8 - "router.tsx" ### Community 8 - "router.tsx"
Cohesion: 0.09 Cohesion: 0.11
Nodes (35): AcceptInvitePage, AccountsPage, BudgetPage, ImportDetailPage, ImportsPage, LoginPage, OperationsPage, RequireAuth() (+27 more) Nodes (35): AcceptInvitePage, LoginPage, RequireAuth(), ResetPasswordPage, SettingsPage, AppLayout(), NAV_ITEMS, LangSwitcher() (+27 more)
### Community 9 - "ledger/router.py" ### Community 9 - "ledger/router.py"
Cohesion: 0.15 Cohesion: 0.15
Nodes (31): account_json(), AccountBody, accounts(), archive_account(), create_account(), create_operation(), delete_account(), operation_json() (+23 more) Nodes (31): account_json(), AccountBody, accounts(), archive_account(), create_account(), create_operation(), delete_account(), operation_json() (+23 more)
### Community 10 - "AccountsPage.tsx" ### Community 10 - "main.py"
Cohesion: 0.21 Cohesion: 0.21
Nodes (13): AccountType, CreateAccountPayload, ConfirmDialog(), ConfirmDialogProps, Modal(), ModalProps, useAccounts(), useArchiveAccount() (+5 more) Nodes (14): get_engine(), app_error_handler(), health(), healthz(), get, Request, readiness(), security_headers() (+6 more)
### Community 11 - "compilerOptions" ### Community 11 - "compilerOptions"
Cohesion: 0.07 Cohesion: 0.07
Nodes (28): compilerOptions, baseUrl, esModuleInterop, forceConsistentCasingInFileNames, isolatedModules, jsx, lib, module (+20 more) Nodes (28): compilerOptions, baseUrl, esModuleInterop, forceConsistentCasingInFileNames, isolatedModules, jsx, lib, module (+20 more)
### Community 12 - "AnalyticsPage.tsx" ### Community 12 - "AnalyticsPage.tsx"
Cohesion: 0.14 Cohesion: 0.21
Nodes (15): AnalyticsPage, NotFoundPage, PlanFactBars(), PlanFactBarsProps, PlanFactRow, MonthSwitcher(), MonthSwitcherProps, PageHeader() (+7 more) Nodes (12): AnalyticsPage, PlanFactBars(), PlanFactBarsProps, PlanFactRow, MonthSwitcher(), MonthSwitcherProps, useAnalyticsBalances(), useAnalyticsCashFlow() (+4 more)
### Community 13 - "analytics/router.py" ### Community 13 - "exports/router.py"
Cohesion: 0.27 Cohesion: 0.27
Nodes (12): balances(), cash_flow(), category_spend(), plan_fact(), Context, date, Db, get (+4 more) Nodes (10): download(), export_status(), Context, Csrf, Db, get, post, Response (+2 more)
### Community 14 - "devDependencies" ### Community 14 - "devDependencies"
Cohesion: 0.11 Cohesion: 0.11
Nodes (19): eslint, @eslint/js, eslint-plugin-jsx-a11y, devDependencies, eslint, @eslint/js, eslint-plugin-jsx-a11y, globals (+11 more) Nodes (19): eslint, @eslint/js, eslint-plugin-jsx-a11y, devDependencies, eslint, @eslint/js, eslint-plugin-jsx-a11y, globals (+11 more)
### Community 15 - "test_import_holds.py" ### Community 15 - "Session"
Cohesion: 0.13 Cohesion: 0.09
Nodes (43): AppError, Domain error rendered as a machine-readable JSON error payload., normalize_description(), date, record_fingerprint(), add_record(), _draft(), hold_document() (+35 more) Nodes (87): balances(), cash_flow(), category_spend(), plan_fact(), date, UUID, BudgetPeriod, Calendar month budget period in the space timezone (month granularity). (+79 more)
### Community 16 - "dependencies" ### Community 16 - "dependencies"
Cohesion: 0.11 Cohesion: 0.11
Nodes (19): dependencies, @hookform/resolvers, i18next, react, react-dom, react-hook-form, react-i18next, react-router-dom (+11 more) Nodes (19): dependencies, @hookform/resolvers, i18next, react, react-dom, react-hook-form, react-i18next, react-router-dom (+11 more)
### Community 17 - "ImportDetailPage.tsx" ### Community 17 - "ImportDetailPage.tsx"
Cohesion: 0.33 Cohesion: 0.27
Nodes (13): useToast(), useConfirmImport(), useImport(), useImportErrorToaster(), useReparseImport(), useRevertImport(), useSaveRowDecision(), useUploadImport() (+5 more) Nodes (15): RowDecision, ImportDetailPage, useToast(), useConfirmImport(), useImport(), useImportErrorToaster(), useReparseImport(), useRevertImport() (+7 more)
### Community 18 - "OperationsPage.tsx" ### Community 18 - "OperationsPage.tsx"
Cohesion: 0.14 Cohesion: 0.11
Nodes (27): CreateOperationPayload, Badge(), BadgeTone, EmptyState(), EmptyStateProps, QueryGate(), QueryGateProps, useActiveAccounts() (+19 more) Nodes (29): Operation, ImportsPage, NotFoundPage, OperationsPage, ReviewQueuePage, Badge(), BadgeTone, EmptyState() (+21 more)
### Community 19 - "SettingsPage.tsx" ### Community 19 - "useOperations.ts"
Cohesion: 0.16 Cohesion: 0.16
Nodes (16): invalidationRoots, queryKeys, SESSION_ROOT, sessionInvalidationRoot, SPACE_ROOT, spaceInvalidationRoot, OperationFilters, useCancelDeletion() (+8 more) Nodes (14): invalidationRoots, queryKeys, SESSION_ROOT, sessionInvalidationRoot, SPACE_ROOT, spaceInvalidationRoot, OperationFilters, useCategorizeOperation() (+6 more)
### Community 20 - "Toaster.tsx" ### Community 20 - "Toaster.tsx"
Cohesion: 0.12 Cohesion: 0.12
Nodes (15): Locale, App(), queryClient, AppRoutes(), ToastApi, ToastContext, ToastItem, ToastKind (+7 more) Nodes (15): Locale, App(), queryClient, AppRoutes(), ToastApi, ToastContext, ToastItem, ToastKind (+7 more)
### Community 21 - "ozon_pdf.py" ### Community 21 - "adapters/base.py"
Cohesion: 0.14 Cohesion: 0.14
Nodes (13): Protocol, StatementAdapter, StatementDraft, _first_page_text(), _ozon_cell(), _ozon_normalize(), OzonCreditPdfAdapter, OzonDebitPdfAdapter (+5 more) Nodes (18): account_fingerprint(), amount_to_kopecks(), _mask_digits(), money_to_kopecks(), Parse statement money strings like '-1 234,56 RUR', '+ 500.00 ₽', '12 345'.…, SourceRow, StatementDraft, _first_page_text() (+10 more)
### Community 22 - "BudgetPage.tsx" ### Community 22 - "BudgetPage.tsx"
Cohesion: 0.36 Cohesion: 0.21
Nodes (12): useBudgetCategories(), useBudgetErrorToaster(), useBudgetSummary(), useCloseMonth(), useCopyPreviousMonth(), useFundCategory(), usePeriodMutation(), useReopenMonth() (+4 more) Nodes (17): BudgetPage, ConfirmDialog(), ConfirmDialogProps, Modal(), ModalProps, useBudgetCategories(), useBudgetErrorToaster(), useBudgetSummary() (+9 more)
### Community 23 - "client.ts" ### Community 23 - "client.ts"
Cohesion: 0.23 Cohesion: 0.23
Nodes (10): apiClient, ApiError, buildUrl(), ErrorEnvelope, parseErrorEnvelope(), QueryValue, readCsrfToken(), request() (+2 more) Nodes (10): apiClient, ApiError, buildUrl(), ErrorEnvelope, parseErrorEnvelope(), QueryValue, readCsrfToken(), request() (+2 more)
### Community 24 - "fields.tsx" ### Community 24 - "AccountsPage.tsx"
Cohesion: 0.21 Cohesion: 0.13
Nodes (11): FieldChrome, InputProps, MoneyField(), MoneyFieldProps, SelectField(), SelectFieldProps, SelectProps, TextareaField() (+3 more) Nodes (21): AccountType, CreateAccountPayload, AccountsPage, FieldChrome, InputProps, MoneyField(), MoneyFieldProps, SelectField() (+13 more)
### Community 25 - "Docker Compose Development Stack" ### Community 25 - "Docker Compose Development Stack"
Cohesion: 0.29 Cohesion: 0.29
@@ -231,17 +234,17 @@ Nodes (12): Credit Reserve, Financial Account, Financial Space, Membership, User
Cohesion: 0.22 Cohesion: 0.22
Nodes (9): scripts, api:generate, build, dev, lint, preview, test, test:watch (+1 more) Nodes (9): scripts, api:generate, build, dev, lint, preview, test, test:watch (+1 more)
### Community 28 - "dependencies.py" ### Community 28 - "exports/service.py"
Cohesion: 0.15 Cohesion: 0.40
Nodes (21): current_context(), current_session(), alias, Context, CurrentSession, Db, Header, User (+13 more) Nodes (9): build_export(), consume_export(), _csv_bytes(), get_export(), _json_rows(), datetime, UUID, request_export() (+1 more)
### Community 29 - "General Pool" ### Community 29 - "General Pool"
Cohesion: 0.29 Cohesion: 0.29
Nodes (8): Budget Period, Funding, General Pool, Monthly Category, Plan Assignment, Savings Envelope, Immutable Closure Snapshots, Plan and Funding Separation Nodes (8): Budget Period, Funding, General Pool, Monthly Category, Plan Assignment, Savings Envelope, Immutable Closure Snapshots, Plan and Funding Separation
### Community 30 - "TestBaseContract" ### Community 30 - "test_adapter_contracts.py"
Cohesion: 0.17 Cohesion: 0.13
Nodes (5): detect_adapter(), make_draft(), make_source_row(), TestBaseContract, Workbook Nodes (10): AlfaCreditXlsxAdapter, Protocol, StatementAdapter, detect_adapter(), OzonCreditPdfAdapter, make_draft(), make_source_row(), Sanitized synthetic contract tests for the evidenced statement adapters. These… (+2 more)
### Community 31 - "Per-File Envelope Encryption" ### Community 31 - "Per-File Envelope Encryption"
Cohesion: 0.29 Cohesion: 0.29
@@ -271,9 +274,9 @@ Nodes (4): name, private, type, version
Cohesion: 0.18 Cohesion: 0.18
Nodes (11): Audited Break-Glass Access, Cross-Space Referential Integrity, Defense-in-Depth Authorization, Financial Space Ownership, Transaction-Local RLS Context, Budget Invariants, Critical User Path, Financial Tracker MVP (+3 more) Nodes (11): Audited Break-Glass Access, Cross-Space Referential Integrity, Defense-in-Depth Authorization, Financial Space Ownership, Transaction-Local RLS Context, Budget Invariants, Critical User Path, Financial Tracker MVP (+3 more)
### Community 38 - "test_adapter_contracts.py" ### Community 38 - "TestRocketbankDebitContract"
Cohesion: 0.11 Cohesion: 0.24
Nodes (9): minimal_pdf(), _QuotaSettings, Sanitized synthetic contract tests for the evidenced statement adapters. These…, Build a small valid single-font PDF with empty or ASCII-only pages., same_page_rocket_lines(), synthetic_rocket_lines(), TestMoneyParsing, TestRocketbankDebitContract (+1 more) Nodes (3): same_page_rocket_lines(), synthetic_rocket_lines(), TestRocketbankDebitContract
### Community 39 - "generate-api.mjs" ### Community 39 - "generate-api.mjs"
Cohesion: 0.40 Cohesion: 0.40
@@ -288,28 +291,28 @@ Cohesion: 0.67
Nodes (3): TestClient, test_invite_login_and_authenticated_space(), test_openapi_contains_critical_paths() Nodes (3): TestClient, test_invite_login_and_authenticated_space(), test_openapi_contains_critical_paths()
### Community 44 - "test_private_fixtures.py" ### Community 44 - "test_private_fixtures.py"
Cohesion: 0.12 Cohesion: 0.13
Nodes (22): magic_kind(), _draft_json(), Any, Reconcile parsed rows against declared bank controls with stable diagnostics.…, _reconciliation_summary(), _check(), _fail(), _load_entries() (+14 more) Nodes (20): Any, Reconcile parsed rows against declared bank controls with stable diagnostics.…, _reconciliation_summary(), _check(), _fail(), _load_entries(), parsed(), fixture (+12 more)
### Community 45 - "TestAlfaDebitContract" ### Community 45 - "TestAlfaDebitContract"
Cohesion: 0.21 Cohesion: 0.19
Nodes (3): alfa_workbook_bytes(), synthetic_alfa_rows(), TestAlfaDebitContract Nodes (3): alfa_workbook_bytes(), synthetic_alfa_rows(), TestAlfaDebitContract
### Community 47 - "graphify" ### Community 47 - "graphify"
Cohesion: 0.17 Cohesion: 0.17
Nodes (11): command, enabled, timeout, type, mcp, graphify, plugin, $schema (+3 more) Nodes (11): command, enabled, timeout, type, mcp, graphify, plugin, $schema (+3 more)
### Community 50 - "SourceRow" ### Community 50 - "OzonDebitPdfAdapter"
Cohesion: 0.19 Cohesion: 0.29
Nodes (9): SourceRow, _date_from(), _PendingRecord, date, Rocketbank debit statement contract, evidenced against the real document.…, RocketbankDebitPdfAdapter, open_pdf(), _quota_error() (+1 more) Nodes (5): _ozon_cell(), _ozon_normalize(), OzonDebitPdfAdapter, Ozon debit statement contract, evidenced against the real document. A 'Справка…, Pattern
### Community 62 - "LocalDirectoryBlobStore" ### Community 62 - "LocalDirectoryBlobStore"
Cohesion: 0.39 Cohesion: 0.39
Nodes (3): LocalDirectoryBlobStore, Path, S3-neutral local store; production swaps in an S3-compatible client. Nodes (3): LocalDirectoryBlobStore, Path, S3-neutral local store; production swaps in an S3-compatible client.
### Community 63 - "account_fingerprint" ### Community 63 - "TestOzonDebitContract"
Cohesion: 0.25 Cohesion: 0.15
Nodes (3): account_fingerprint(), ozon_extracted(), TestOzonDebitContract Nodes (6): minimal_pdf(), ozon_extracted(), _QuotaSettings, Build a small valid single-font PDF with empty or ASCII-only pages., TestOzonDebitContract, MonkeyPatch
### Community 64 - "Gitea CI and real statement imports" ### Community 64 - "Gitea CI and real statement imports"
Cohesion: 0.29 Cohesion: 0.29
@@ -319,41 +322,41 @@ Nodes (6): Confirmed decisions, Gitea CI and real statement imports, Goal and co
Cohesion: 0.80 Cohesion: 0.80
Nodes (4): downgrade(), _has_check_constraint(), _has_column(), upgrade() Nodes (4): downgrade(), _has_check_constraint(), _has_column(), upgrade()
### Community 71 - "spaces/router.py" ### Community 71 - "ColumnChart.tsx"
Cohesion: 0.16
Nodes (19): send_token_email(), cancel_deletion(), current(), deletion_status(), DeletionBody, invite_member(), MemberInviteBody, alias (+11 more)
### Community 72 - "identity/service.py"
Cohesion: 0.28
Nodes (18): unauthorized(), accept_invitation(), authenticate(), create_invitation(), create_password_reset(), create_session(), get_active_session(), normalize_email() (+10 more)
### Community 73 - "get_settings"
Cohesion: 0.16
Nodes (12): get_settings(), Settings, create_celery_app(), encrypt_file(), EncryptedFile, Per-file random DEK; DEK wrapped with the KEK from the secret manager., Crypto-shred hook: deleting wrapped DEK rows in DB plus blobs removes access., shred_dek_material() (+4 more)
### Community 74 - "ColumnChart.tsx"
Cohesion: 0.24 Cohesion: 0.24
Nodes (7): CashFlowColumn, ColumnChart(), ColumnChartProps, BarRow, HorizontalBars(), HorizontalBarsProps, rows Nodes (7): CashFlowColumn, ColumnChart(), ColumnChartProps, BarRow, HorizontalBars(), HorizontalBarsProps, rows
### Community 72 - "conftest.py"
Cohesion: 0.52
Nodes (6): client(), db(), db_factory(), fixture, sessionmaker, TestClient
### Community 73 - "imports/service.py"
Cohesion: 0.13
Nodes (20): get_settings(), Settings, normalize_description(), date, record_fingerprint(), magic_kind(), get_blob_store(), new_object_key() (+12 more)
### Community 75 - "AppError"
Cohesion: 0.43
Nodes (5): AppError, Domain error rendered as a machine-readable JSON error payload., unprocessable(), _quota_error(), Exception
## Knowledge Gaps ## Knowledge Gaps
- **185 isolated node(s):** `$schema`, `file:///home/k1nq/repos/finance/.kilo/plugins/graphify.js`, `type`, `/home/k1nq/.local/bin/graphify-mcp`, `/home/k1nq/repos/finance/graphify-out/graph.json` (+180 more) - **186 isolated node(s):** `$schema`, `file:///home/k1nq/repos/finance/.kilo/plugins/graphify.js`, `type`, `/home/k1nq/.local/bin/graphify-mcp`, `/home/k1nq/repos/finance/graphify-out/graph.json` (+181 more)
These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 335 node(s) total have ≤1 connection when file, concept and rationale nodes are included.) These have ≤1 connection - possible missing edges or undocumented components. (Counts symbols only; 339 node(s) total have ≤1 connection when file, concept and rationale nodes are included.)
- **15 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes. - **18 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions ## Suggested Questions
_Questions this graph is uniquely positioned to answer:_ _Questions this graph is uniquely positioned to answer:_
- **Why does `AppError` connect `test_import_holds.py` to `identity/router.py`, `Base`, `set_space_context`, `imports/router.py`, `alfa_xlsx.py`, `Session`, `test_adapter_contracts.py`, `identity/service.py`, `get_settings`, `SourceRow`, `dependencies.py`, `account_fingerprint`?** - **Why does `Session` connect `Session` to `identity/service.py`, `Base`, `set_space_context`, `ledger/service.py`, `conftest.py`, `imports/service.py`, `exports/service.py`?**
_High betweenness centrality (0.078) - this node is a cross-community bridge._ _High betweenness centrality (0.085) - this node is a cross-community bridge._
- **Why does `Session` connect `Session` to `Base`, `set_space_context`, `identity/service.py`, `analytics/router.py`, `test_import_holds.py`, `dependencies.py`?** - **Why does `AppError` connect `AppError` to `identity/service.py`, `Base`, `imports/router.py`, `alfa_xlsx.py`, `ledger/service.py`, `TestRocketbankDebitContract`, `imports/service.py`, `main.py`, `exports/router.py`, `Session`, `adapters/base.py`, `test_adapter_contracts.py`, `TestOzonDebitContract`?**
_High betweenness centrality (0.074) - this node is a cross-community bridge._ _High betweenness centrality (0.069) - this node is a cross-community bridge._
- **Why does `bad_request()` connect `Session` to `budgeting/router.py`, `spaces/router.py`, `identity/service.py`, `ledger/router.py`, `test_import_holds.py`?** - **Why does `bad_request()` connect `ledger/service.py` to `identity/service.py`, `budgeting/router.py`, `imports/service.py`, `ledger/router.py`, `AppError`, `Session`?**
_High betweenness centrality (0.025) - this node is a cross-community bridge._ _High betweenness centrality (0.017) - this node is a cross-community bridge._
- **Are the 2 inferred relationships involving `Session` (e.g. with `UTCDateTime` and `test_postgres_rls_requires_matching_transaction_context()`) actually correct?** - **Are the 2 inferred relationships involving `Session` (e.g. with `UTCDateTime` and `test_postgres_rls_requires_matching_transaction_context()`) actually correct?**
_`Session` has 2 INFERRED edges - model-reasoned connections that need verification._ _`Session` has 2 INFERRED edges - model-reasoned connections that need verification._
- **Are the 23 inferred relationships involving `AppError` (e.g. with `request_export()` and `login()`) actually correct?** - **Are the 24 inferred relationships involving `AppError` (e.g. with `request_export()` and `login()`) actually correct?**
_`AppError` has 23 INFERRED edges - model-reasoned connections that need verification._ _`AppError` has 24 INFERRED edges - model-reasoned connections that need verification._
- **Are the 3 inferred relationships involving `Base` (e.g. with `downgrade()` and `upgrade()`) actually correct?** - **Are the 3 inferred relationships involving `Base` (e.g. with `downgrade()` and `upgrade()`) actually correct?**
_`Base` has 3 INFERRED edges - model-reasoned connections that need verification._ _`Base` has 3 INFERRED edges - model-reasoned connections that need verification._
- **What connects `$schema`, `file:///home/k1nq/repos/finance/.kilo/plugins/graphify.js`, `type` to the rest of the system?** - **What connects `$schema`, `file:///home/k1nq/repos/finance/.kilo/plugins/graphify.js`, `type` to the rest of the system?**
_185 weakly-connected nodes found - possible documentation gaps or missing edges._ _186 weakly-connected nodes found - possible documentation gaps or missing edges._
File diff suppressed because one or more lines are too long
+4772 -4218
View File
File diff suppressed because it is too large Load Diff
+22 -10
View File
@@ -264,15 +264,15 @@
"semantic_hash": "0904afccafd9eb544b4d6846fc6384c8" "semantic_hash": "0904afccafd9eb544b4d6846fc6384c8"
}, },
"backend/app/imports/router.py": { "backend/app/imports/router.py": {
"mtime": 1789730952.1978154, "mtime": 1789987595.224261,
"seen": 1789735433.1581337, "seen": 1789987623.1673603,
"ast_hash": "5bd2a94019f9cbda6d0dc617dc82cd41", "ast_hash": "561967cd45b365aeeb5f06dcba55e0a3",
"semantic_hash": "" "semantic_hash": ""
}, },
"backend/app/imports/service.py": { "backend/app/imports/service.py": {
"mtime": 1789736072.1450737, "mtime": 1789987595.224261,
"seen": 1789736272.9455285, "seen": 1789987623.1673615,
"ast_hash": "16c0182cdcfb0aee9a7a5a895e3c86b2", "ast_hash": "d78aede9091025373227eeea80758ffb",
"semantic_hash": "" "semantic_hash": ""
}, },
"backend/app/imports/tasks.py": { "backend/app/imports/tasks.py": {
@@ -378,10 +378,10 @@
"semantic_hash": "f6e5c4d1e1d0f2b1941acf8ec9f6678b" "semantic_hash": "f6e5c4d1e1d0f2b1941acf8ec9f6678b"
}, },
"backend/tests/test_imports.py": { "backend/tests/test_imports.py": {
"mtime": 1789358115.6295624, "mtime": 1789987559.2557845,
"seen": 1789377303.0692356, "seen": 1789987623.1673782,
"ast_hash": "34ad7bb7ce983ff5fd4f8b3afc3d9f8b", "ast_hash": "775b6bb13c49cfd933fdfe981142dc41",
"semantic_hash": "34ad7bb7ce983ff5fd4f8b3afc3d9f8b" "semantic_hash": ""
}, },
"backend/tests/test_ledger_properties.py": { "backend/tests/test_ledger_properties.py": {
"mtime": 1789129573.0755556, "mtime": 1789129573.0755556,
@@ -922,5 +922,17 @@
"seen": 1789735433.1582298, "seen": 1789735433.1582298,
"ast_hash": "2ae2370fa527b015cd7bd98dc7ca2a6b", "ast_hash": "2ae2370fa527b015cd7bd98dc7ca2a6b",
"semantic_hash": "" "semantic_hash": ""
},
".kilo/plugins/graphify.js": {
"mtime": 1789967188.4955833,
"seen": 1789987085.8116593,
"ast_hash": "af79b534b1739d62b450e39c9b50e362",
"semantic_hash": ""
},
"AGENTS.md": {
"mtime": 1789967188.495481,
"seen": 1789987085.8117683,
"ast_hash": "6a47d64fcc136324ca395065a707ef76",
"semantic_hash": ""
} }
} }