Bootstrap fin-hub scaffold from hub-core (CUST-WP-0025-T22/T23)

Add hub-core editable dependency, fin-specific SQLAlchemy models, and smoke tests.
This commit is contained in:
tegwick 2026-07-08 00:50:24 +02:00
commit b6993d4a05
10 changed files with 1737 additions and 0 deletions

View file

@ -0,0 +1,11 @@
"""Fin-specific SQLAlchemy models (T23 implementation target)."""
from fin_hub.models.budget import Budget, BurnRate, Commitment, RunwayProjection, TokenSpend
__all__ = [
"Budget",
"Commitment",
"BurnRate",
"RunwayProjection",
"TokenSpend",
]

View file

@ -0,0 +1,77 @@
"""Financial viability models — initial schema for CUST-WP-0025-T23."""
from __future__ import annotations
import uuid
from datetime import date, datetime
from sqlalchemy import Date, DateTime, Float, Integer, String, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from hub_core.models.base import Base, TimestampMixin
class Budget(Base, TimestampMixin):
__tablename__ = "fin_budgets"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_start: Mapped[date] = mapped_column(Date, nullable=False)
period_end: Mapped[date] = mapped_column(Date, nullable=False)
allocated: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
committed: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
spent: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
class Commitment(Base, TimestampMixin):
__tablename__ = "fin_commitments"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
commitment_type: Mapped[str] = mapped_column(String(32), nullable=False)
title: Mapped[str] = mapped_column(String(256), nullable=False)
amount: Mapped[float] = mapped_column(Float, nullable=False)
cadence: Mapped[str] = mapped_column(String(16), nullable=False, default="monthly")
start_date: Mapped[date] = mapped_column(Date, nullable=False)
end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
class BurnRate(Base, TimestampMixin):
__tablename__ = "fin_burn_rates"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
domain_slug: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
period_month: Mapped[str] = mapped_column(String(7), nullable=False, index=True)
actual_spend: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
projected_spend: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
class RunwayProjection(Base, TimestampMixin):
__tablename__ = "fin_runway_projections"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
current_balance: Mapped[float] = mapped_column(Float, nullable=False)
monthly_burn: Mapped[float] = mapped_column(Float, nullable=False)
months_remaining: Mapped[float] = mapped_column(Float, nullable=False)
alert_threshold_months: Mapped[float] = mapped_column(Float, nullable=False, default=3.0)
computed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")
class TokenSpend(Base, TimestampMixin):
__tablename__ = "fin_token_spends"
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
provider: Mapped[str] = mapped_column(String(32), nullable=False, index=True)
model: Mapped[str] = mapped_column(String(64), nullable=False)
tokens_in: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
tokens_out: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
cost: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
session_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
currency: Mapped[str] = mapped_column(String(3), nullable=False, default="EUR")