Docs & rules

GiuseppeDM98/net-worth-trackerGitHub

This is a public, read-only report. Striff reads this repository's docs, turns each sentence that makes a claim about the code into a rule, and checks the rule against the code on the default branch. How this works

main3e5f0c7Last read 2h ago

Is this yours? Install to manage it

Once installed, Striff checks every pull request.

Documents
Rules
61
54
54
3
3
1
19
9
7
7
5
5
2
20 docs
GiuseppeDM98/net-worth-trackerOpen repository

GiuseppeDM98/net-worth-tracker — documented rules

61 of 61 rules, printed November 3, 2026.

The sentence in your docs
NarrativeSegments draws a figure's unit («€», «pt», «pp») as its own span at ml-[0.2em] (splitFigureUnit, components/ui/narrative-text.tsx; the span carries data-figure-unit, which is how the first-screen census still counts the pair as ONE figure — doc/guide/prima-schermata.md), because in Geist Mono the no-break space is a whole cell.
NarrativeSegments depends on splitFigureUnit
Holds
recharts only from components/ui/charts/recharts.ts, which binds export const X = RechartsX — Turbopack batches a library by where it is ENTERED (four identical 350 KB copies, one per page) and a barrel of bare re-exports is transparent to it;
ts imports recharts
Holds
Expense.personalMemberId;
Expense has personalMemberId
Holds
Asset.allocationRole is ONE field, THREE values: tradable (default), frozen (in the denominator, never in the plans), excluded (out of the page entirely);
Asset has an allocationRole field
Holds
A free question with NO context still gets the portfolio of today (buildAssistantTodayContext → AssistantTodayContext, the sixth argument of buildChatPrompt, 2026-10-20): the period bundle is a chosen period's, never a substitute for the portfolio.
buildChatPrompt depends on AssistantTodayContext
Holdslib types
Patrimonio: «cosa possiedo, e cosa si è mosso?» — verdict, six tiles, «Mutuo»/«Prestito» per loan (a debt is a loan asset worth minus its principal, 2026-10-10), «Intestatari» (Asset.ownerMemberId, absent = in comune, lib/utils/assetOwnership.ts, 2026-10-17; on the value or on the price of carry, 2026-10-18) and its Strumenti filter, Strumenti; composed for a phone since 2026-10-14.
Asset has ownerMemberId
Holds
Cashflow › Budget: «sto rispettando il budget?» — a row «Fuori dal budget» (Expense.excludeFromBudget, the ONE gate countsTowardBudget, 2026-10-17) counts everywhere but here. doc/guide/cashflow-budget.md.
Expense has excludeFromBudget
Holds
describeLoginStatus / describeRegisterStatus return an AuthReading — the words plus a tone — for idle · submitting · success · error, and AuthStatusLine paints the negative tone text-destructive. Two traps.
2 rules from this sentence
describeLoginStatus depends on AuthReading
Holds
describeRegisterStatus depends on AuthReading
Holds
The displayName fallback for email/password users (completeDisplayName → resolveDisplayName, lib/utils/authProfile.ts) runs after: it patches user only while the same uid is still signed in, and copies the name into the Auth profile once (updateProfile, best effort, never for NEXT_PUBLIC_DEMO_USER_ID), so the next sign-in needs no read.
completeDisplayName depends on resolveDisplayName
Holdscontexts lib
bundle.portfolio is REQUIRED and built by ONE pure function, buildAssistantPortfolioContext (lib/utils/assistantPortfolioContext.ts), from the documents every builder fetches beside the assets (fetchPortfolioDocuments: assetTransactions, dividends, pensionContributions) and the period's expenses: every held instrument with class, sub-category, ROLE, quantity, value, fiscal PMC and G/P in euro, owner (describeAssetOwnerLabel), liquidity;
buildAssistantPortfolioContext is in lib/utils
Holds
buildAssistantTodayContext(userId) returns AssistantTodayContext — the bundle's portfolio, goals, targetAllocation, targetAllocationSource, from the same documents and the same pure functions as the five period builders (no expenses: the loans' «ultima rata nel periodo» needs a period; no snapshot, no cashflow).
buildAssistantTodayContext depends on AssistantTodayContext
Holdslib types
buildChatPrompt takes it as its sixth argument and uses it only when there is no bundle. The route passes it as todayContext to streamAssistantResponse and sends NO context SSE event: the page's tiles are a period's, and «Patrimonio oggi» already stands in.
buildChatPrompt depends on AssistantTodayContext
Holdslib types
lib/utils/demoAccount.ts (isDemoUid), read by lib/hooks/useDemoMode.ts and by isPersistableQuery — doc/guide/account-condiviso-demo.md
isPersistableQuery depends on isDemoUid
Holdsconstants utils
Budget components/cashflow/BudgetTab.tsx + components/cashflow/budget/* (BudgetTrack, BudgetDeleteButton, BudgetItemDialog), pure lib/utils/{budgetSummary,budgetNarrative,budgetUtils,budgetHistory}.ts, specs e2e/cashflow.budget{,.mobile}.spec.ts, lib/hooks/{useBudgetConfig,useBudgetHistory}.ts, lib/server/budgetHistoryService.ts (cron phase 8), collections budgets/{userId},
5 rules from this sentence
BudgetTrack is in components/cashflow/budget
Holds
BudgetDeleteButton is in components/cashflow/budget
Holds
BudgetItemDialog is in components/cashflow/budget
Holds
useBudgetConfig is in lib/hooks
checks “lib/hooks/{useBudgetConfig,useBudgetHistory}.ts”
Holds
useBudgetHistory is in lib/hooks
checks “lib/hooks/{useBudgetConfig,useBudgetHistory}.ts”
Holds
budgetUtils, budgetSummary, budgetNarrative (with describeBudgetClosedAnswers, the landscape index), tabletComposition (+ patrimonioNarrative for the articles, weeklyBudgetEmailService, monthlyEmailService)
describeBudgetClosedAnswers is in lib/utils
Holds
Opt-in: reconcileBudgetItems only refreshes denormalized names and drops orphans, never auto-creates. BudgetItem fields are all required, fixtures included: amount, period, kind, order.
4 rules from this sentence
BudgetItem has an amount field
Holds
BudgetItem has a period field
Holds
BudgetItem has a kind field
Holds
BudgetItem has an order field
Holds
A threshold is a fact of what is BOOKED, read against its own calendar (2026-09-14): BudgetAlert.spent is the spend up to today (splitMonthActualForItem / splitYearActualForItem, the ceiling on split.spentToDate), so a mortgage dated the 27th crosses nothing on the 14th;
BudgetAlert has spent
Holds
every alert carries period, calendarPct (the month's or the year's elapsed share, yearElapsedPctAt) and aheadOfCalendar (judged on the printed integers), and the tile paints text-warning-foreground only on the rows that are ahead — «Tecnologia 54% · soglia 50% · anno al 70%» is muted.
BudgetAlert has period, calendarPct and aheadOfCalendar
Holds
«Speso» is the booked part, «in calendario» its own clause (DESIGN.md → The Scheduled-Is-Not-Spent Rule, 2026-09-14): CeilingSummary.spent still includes the scheduled rows — the ratios, remaining, exceeded and the fill colour read it — but no sentence prints it as spending.
CeilingSummary has spent
Holds
Both feed CeilingSummary (crossedOn, projectedCrossingDay, overBy, dailyPace, sustainablePace) and BudgetAlert.crossedOn (monthly items and the ceiling; annual budgets cross on a date of the year, a sentence not told yet).
6 rules from this sentence
CeilingSummary has crossedOn
checks “Both feed CeilingSummary (crossedOn, projectedCrossingDay, overBy, dailyPace, sustainablePace)”
Holds
CeilingSummary has projectedCrossingDay
checks “Both feed CeilingSummary (crossedOn, projectedCrossingDay, overBy, dailyPace, sustainablePace)”
Holds
CeilingSummary has overBy
checks “Both feed CeilingSummary (crossedOn, projectedCrossingDay, overBy, dailyPace, sustainablePace)”
Holds
CeilingSummary has dailyPace
checks “Both feed CeilingSummary (crossedOn, projectedCrossingDay, overBy, dailyPace, sustainablePace)”
Holds
CeilingSummary has sustainablePace
checks “Both feed CeilingSummary (crossedOn, projectedCrossingDay, overBy, dailyPace, sustainablePace)”
Holds
BudgetAlert has crossedOn
checks “BudgetAlert.crossedOn”
Holds
«Legata all'immobile» (Expense.propertyAssetId): the field in components/expenses/ExpenseDialog.tsx («Impostazioni avanzate»), «Collega la serie a un immobile…» in components/cashflow/TransactionFeed.tsx → components/expenses/LinkSeriesDialog.tsx (target: 'property') → linkSeriesToProperty (lib/services/expenseService.ts).
2 rules from this sentence
LinkSeriesDialog depends on linkSeriesToProperty
checks “components/expenses/LinkSeriesDialog.tsx (target: 'property') → linkSeriesToProperty”
Holdscomponents lib
Expense has a propertyAssetId field
checks “Expense.propertyAssetId”
Holds
Setting: AssetAllocationSettings.realEstateCostCategories (Impostazioni › Spese › «Compravendita», RealEstateDefaultsTile; the five places in doc/guide/impostazioni.md).
AssetAllocationSettings has realEstateCostCategories
Holds
The Cashflow section DECLARES that floor in its scope line and in a note (historyFloorYear on CashflowData, set only for a Totale export): a reader told "Totale" otherwise reads the missing years as years without spending (DESIGN → The Declared-Window Rule).
CashflowData has historyFloorYear
Holds
The Rendimenti section measures the SAME base as the page (2026-09-07, issue #324): preparePerformanceData receives the assets, reads the pension contributions and resolves resolvePerformanceBase — projected snapshots, pension and measured flows — before calling calculatePerformanceForPeriod;
preparePerformanceData depends on resolvePerformanceBase, and preparePerformanceData depends on calculatePerformanceForPeriod
Holdsservices utils
A section's reading must not mix two windows. HistoryData carries netWorthEvolution (the filtered series the page tabulates) AND totalGrowth (measured between oldestSnapshot and latestSnapshot);
2 rules from this sentence
HistoryData has netWorthEvolution
checks “HistoryData carries netWorthEvolution”
Holds
HistoryData has totalGrowth
checks “HistoryData carries”
Holds
The prompt BODY is the assistant's own block: buildEmailAiPrompt = formatBundleForPrompt(bundle, label) + the sections only the email has (market effect, comparisons, category deltas, Hall of Fame, budget alerts).
buildEmailAiPrompt depends on formatBundleForPrompt
HoldsmonthlyEmailService.ts assistant
…and the user's money that moved outside the cashflow (owner, 2026-10-14): marketEffectOf also subtracts MonthlyEmailData.periodFlows
marketEffectOf depends on MonthlyEmailData
Holds
EmailRankedRow.trailingSign colours the optional THIRD column, not the amount;
EmailRankedRow has trailingSign
Holds
The hero is ONE component for THREE surfaces now. PatrimonioTile used to take the whole DashboardOverviewPayload and read five fields of it; it takes those five as props (variations, isNewATH, movers, assetCount, hasCurrentMonthSnapshot), because the landing has no account and therefore no payload.
5 rules from this sentence
PatrimonioTileProps has variations
Holds
PatrimonioTileProps has isNewATH
Holds
PatrimonioTileProps has movers
Holds
PatrimonioTileProps has assetCount
Holds
PatrimonioTileProps has hasCurrentMonthSnapshot
Holds
Since 2026-10-11 the shared tile also takes liftedFigures and id (the Panoramica's phone composition) and draws its curve at 120px below desktop: on every surface: the landing passes neither prop, so it keeps every block and only the shorter curve.
2 rules from this sentence
PatrimonioTileProps has liftedFigures
Holds
PatrimonioTileProps has id
Holds
Now the server checks only assertDateNotInFuture; a migrated asset keeps its baseline as the floor through the replay's BASELINE_NOT_FIRST, whose message names the day («Su questo asset le operazioni partono dalla posizione iniziale del 01/01/2024»), and TransactionDialog sets the input's min from the asset's OWN baseline (existingTransactions), never from the meta. The consequences are declared, not hidden: the Rendimenti flows read an instrument's first appearance as its entry (doc/guide/rendimenti.md § THE ENTRY MONTH); a settlement account is debited TODAY whatever the date, so with a date in a past month both dialogs replace the promise with a warning (describeSettlementTiming, lib/utils/dialogNarrative.ts);
TransactionDialog depends on describeSettlementTiming
Holdscomponents lib
A sell credits its account NET of the tax the broker withheld (2026-09-20, AssetTransaction.withheldTaxEur, SELL only, >= 0).
AssetTransaction has withheldTaxEur
Holds
The estimate stands on the gain the BROKER taxes — the price difference, no commission on either side (LedgerTransactionEffect.taxableGainEur, from a fee-free cost basis the replay keeps beside costBasisEur; an adjustment resets it like the PMC).
LedgerTransactionEffect has taxableGainEur
Holds
resolveBondPrice lives in lib/utils/bondPricing.ts (since 2026-09-11; it used to be exported from AssetDialog.tsx) and is REUSED by the dialog, the trade form and the price cron — a trade's pricePerUnit must mean exactly what averageCost means: euro per unit, the nominal defaulting to 1 € (doc/guide/patrimonio.md § Asset Pricing).
2 rules from this sentence
AssetDialog depends on resolveBondPrice
checks “resolveBondPrice lives in lib/utils/bondPricing.ts (since 2026-09-11; it used to be exported from AssetDialog.tsx) and is REUSED by the dialog, the trade form and the price cron”
Holdscomponents lib
resolveBondPrice is in lib/utils
checks “resolveBondPrice lives in lib/utils/bondPricing.ts”
Holds
61 rules from 10 docs

More open-source reports

Every report →

Other public repositories whose docs Striff read and checked against their code.