Adapt economics dashboard to sole-member zero-cost reality

Update membership, revenue, and cost registries to one active member with
no running costs. Refresh dashboard report and tests for €8.99 revenue and
100% gross margin.
This commit is contained in:
2026-06-22 01:41:59 +02:00
parent a1a4aa972f
commit 8f42220d81
6 changed files with 38 additions and 118 deletions

View File

@@ -28,5 +28,6 @@ python3 -m observatory --period 2026-06
python3 -m observatory --period 2026-06 --output reports/economics-2026-06.md
```
Seed data is used until Bubble (Sprint 2) and Stripe (Sprint 3) importers
replace manual entries.
**Current registry state:** one active member (founder), €8.99/month revenue, no
running costs recorded. Bubble (Sprint 2) and Stripe (Sprint 3) importers will
replace manual entries when integrations land.

View File

@@ -1,64 +1,9 @@
{
"version": 1,
"period": "2026-06",
"entries": [
{
"id": "bubble-subscription",
"name": "Bubble.io platform",
"category": "fixed",
"amount": "35.00",
"currency": "USD",
"cadence": "monthly",
"allocation": "flat"
},
{
"id": "domains",
"name": "Domains",
"category": "fixed",
"amount": "15.00",
"currency": "EUR",
"cadence": "monthly",
"allocation": "flat"
},
{
"id": "operational-overhead",
"name": "Operational overhead",
"category": "fixed",
"amount": "25.00",
"currency": "EUR",
"cadence": "monthly",
"allocation": "flat"
},
{
"id": "stripe-percentage",
"name": "Stripe percentage fee",
"category": "variable",
"amount": "0.015",
"currency": "ratio",
"cadence": "per_transaction",
"allocation": "percent_of_gross_revenue"
},
{
"id": "stripe-fixed",
"name": "Stripe fixed fee",
"category": "variable",
"amount": "0.25",
"currency": "EUR",
"cadence": "per_transaction",
"allocation": "per_active_member"
},
{
"id": "openrouter-ai",
"name": "OpenRouter AI consumption",
"category": "variable",
"amount": "0.00",
"currency": "EUR",
"cadence": "monthly",
"allocation": "flat",
"note": "Populated in Sprint 4 after usage import"
}
],
"entries": [],
"fx_rates": {
"USD/EUR": "0.92"
}
},
"note": "No running costs at present. Future fixed/variable lines (Bubble, Stripe, OpenRouter) added as they become payable."
}

View File

@@ -1,40 +1,16 @@
{
"version": 1,
"snapshot_date": "2026-06-21",
"snapshot_date": "2026-06-22",
"members": [
{
"id": "member-001",
"id": "member-founder",
"external_id": null,
"status": "active",
"joined_at": "2025-11-03",
"plan_id": "flat-899-eur-monthly",
"source": "seed"
},
{
"id": "member-002",
"external_id": null,
"status": "active",
"joined_at": "2026-01-15",
"plan_id": "flat-899-eur-monthly",
"source": "seed"
},
{
"id": "member-003",
"external_id": null,
"status": "active",
"joined_at": "2026-03-28",
"plan_id": "flat-899-eur-monthly",
"source": "seed"
},
{
"id": "member-004",
"external_id": null,
"status": "churned",
"joined_at": "2025-08-10",
"churned_at": "2026-02-01",
"plan_id": "flat-899-eur-monthly",
"source": "seed"
"source": "manual",
"note": "Sole active member (founder)"
}
],
"note": "Seed data for Sprint 1; replaced by Bubble importer in Sprint 2"
"note": "Current reality: one active member. Bubble importer (Sprint 2) will sync live data."
}

View File

@@ -2,15 +2,15 @@
"version": 1,
"entries": [
{
"id": "rev-2026-06-estimate",
"id": "rev-2026-06-founder",
"period": "2026-06",
"gross_amount": "80.91",
"fees_amount": "2.46",
"gross_amount": "8.99",
"fees_amount": "0.00",
"refunds_amount": "0.00",
"net_amount": "78.45",
"net_amount": "8.99",
"currency": "EUR",
"source": "manual_estimate",
"note": "Seed estimate for Sprint 1; replaced by Stripe sync in Sprint 3"
"source": "manual",
"note": "Single-member subscription; Stripe sync in Sprint 3"
}
]
}

View File

@@ -8,14 +8,14 @@
| Metric | Value |
|--------|------:|
| Active members | 3 |
| Monthly revenue | 80.91 EUR |
| Monthly cost | 74.16 EUR |
| Cost per member | 24.72 EUR |
| Gross margin | 6.75 EUR |
| Gross margin % | 8.3% |
| Active members | 1 |
| Monthly revenue | 8.99 EUR |
| Monthly cost | 0.00 EUR |
| Cost per member | 0.00 EUR |
| Gross margin | 8.99 EUR |
| Gross margin % | 100.0% |
_Revenue source: manual_estimate_
_Revenue source: manual_
## Pricing Model Registry

View File

@@ -17,10 +17,10 @@ DATA_DIR = Path(__file__).resolve().parent.parent / "data"
def test_active_members_counts_only_active_status() -> None:
members = load_membership(DATA_DIR)
assert active_members(members) == 3
assert active_members(members) == 1
def test_build_snapshot_uses_seed_revenue_for_period() -> None:
def test_build_snapshot_reflects_sole_member_and_zero_costs() -> None:
product = load_product(DATA_DIR)
models = load_pricing_models(DATA_DIR)
members = load_membership(DATA_DIR)
@@ -29,23 +29,20 @@ def test_build_snapshot_uses_seed_revenue_for_period() -> None:
snapshot = build_snapshot("2026-06", product, models, members, revenue, costs, fx_rates)
assert snapshot.active_members == 3
assert snapshot.monthly_revenue == Decimal("80.91")
assert snapshot.revenue_source == "manual_estimate"
assert snapshot.active_members == 1
assert snapshot.monthly_revenue == Decimal("8.99")
assert snapshot.revenue_source == "manual"
assert snapshot.pricing_model_count == 3
assert snapshot.monthly_cost > Decimal("0")
assert snapshot.cost_per_member == (snapshot.monthly_cost / 3).quantize(Decimal("0.01"))
assert snapshot.gross_margin == (snapshot.monthly_revenue - snapshot.monthly_cost).quantize(
Decimal("0.01")
)
assert snapshot.monthly_cost == Decimal("0.00")
assert snapshot.cost_per_member == Decimal("0.00")
assert snapshot.gross_margin == Decimal("8.99")
assert snapshot.gross_margin_pct == Decimal("100.0")
def test_monthly_cost_includes_fixed_and_variable_components() -> None:
def test_monthly_cost_is_zero_with_empty_registry() -> None:
_, costs, fx_rates = load_costs(DATA_DIR)
total = monthly_cost_total(costs, fx_rates, Decimal("80.91"), 3)
# fixed: 35 USD -> 32.20 EUR + 15 EUR + 25 EUR = 72.20
# variable: 1.5% of 80.91 + 0.25 * 3 = 1.21 + 0.75 = 1.96
assert total == Decimal("74.16")
total = monthly_cost_total(costs, fx_rates, Decimal("8.99"), 1)
assert total == Decimal("0.00")
def test_dashboard_module_renders_markdown() -> None:
@@ -54,4 +51,5 @@ def test_dashboard_module_renders_markdown() -> None:
report = generate_dashboard(DATA_DIR, "2026-06")
assert "# Economics Dashboard v1" in report
assert "Pricing Model Registry" in report
assert "flat-899-eur-monthly" in report
assert "flat-899-eur-monthly" in report
assert "Active members | 1" in report