planner-lab 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. planner_lab-0.1.0/.github/workflows/ci.yml +35 -0
  2. planner_lab-0.1.0/.github/workflows/release.yml +21 -0
  3. planner_lab-0.1.0/.gitignore +33 -0
  4. planner_lab-0.1.0/CITATION.cff +16 -0
  5. planner_lab-0.1.0/CLAUDE.md +90 -0
  6. planner_lab-0.1.0/LICENSE +21 -0
  7. planner_lab-0.1.0/PKG-INFO +185 -0
  8. planner_lab-0.1.0/README.md +153 -0
  9. planner_lab-0.1.0/docs/README.md +8 -0
  10. planner_lab-0.1.0/docs/assets/demo.gif +0 -0
  11. planner_lab-0.1.0/docs/case-studies/data-gaps-and-csv-import.md +60 -0
  12. planner_lab-0.1.0/docs/case-studies/data-gaps-memo.audit.json +392 -0
  13. planner_lab-0.1.0/docs/case-studies/data-gaps-memo.md +111 -0
  14. planner_lab-0.1.0/docs/case-studies/on-track-couple-memo.md +100 -0
  15. planner_lab-0.1.0/docs/case-studies/on-track-couple.md +35 -0
  16. planner_lab-0.1.0/docs/case-studies/rejected-memo.md +28 -0
  17. planner_lab-0.1.0/examples/cases/incomplete_household.yaml +22 -0
  18. planner_lab-0.1.0/examples/cases/sample_household.yaml +64 -0
  19. planner_lab-0.1.0/examples/data/sample_transactions_monarch.csv +56 -0
  20. planner_lab-0.1.0/examples/hello_agent.py +67 -0
  21. planner_lab-0.1.0/pyproject.toml +82 -0
  22. planner_lab-0.1.0/scripts/slopcheck.sh +104 -0
  23. planner_lab-0.1.0/src/planner_lab/__init__.py +9 -0
  24. planner_lab-0.1.0/src/planner_lab/adapters/__init__.py +74 -0
  25. planner_lab-0.1.0/src/planner_lab/adapters/csv_import/__init__.py +4 -0
  26. planner_lab-0.1.0/src/planner_lab/adapters/csv_import/importer.py +183 -0
  27. planner_lab-0.1.0/src/planner_lab/adapters/csv_import/mapping.py +66 -0
  28. planner_lab-0.1.0/src/planner_lab/adapters/fundedness_metric/__init__.py +3 -0
  29. planner_lab-0.1.0/src/planner_lab/adapters/fundedness_metric/metric.py +175 -0
  30. planner_lab-0.1.0/src/planner_lab/adapters/lifecycle/__init__.py +3 -0
  31. planner_lab-0.1.0/src/planner_lab/adapters/lifecycle/allocation.py +71 -0
  32. planner_lab-0.1.0/src/planner_lab/adapters/mcp_research/__init__.py +3 -0
  33. planner_lab-0.1.0/src/planner_lab/adapters/mcp_research/source.py +94 -0
  34. planner_lab-0.1.0/src/planner_lab/adapters/monteplan/__init__.py +3 -0
  35. planner_lab-0.1.0/src/planner_lab/adapters/monteplan/simulator.py +161 -0
  36. planner_lab-0.1.0/src/planner_lab/agents/__init__.py +6 -0
  37. planner_lab-0.1.0/src/planner_lab/agents/llm_critic.py +60 -0
  38. planner_lab-0.1.0/src/planner_lab/agents/memo_writer.py +186 -0
  39. planner_lab-0.1.0/src/planner_lab/agents/models.py +30 -0
  40. planner_lab-0.1.0/src/planner_lab/agents/orchestrator.py +64 -0
  41. planner_lab-0.1.0/src/planner_lab/agents/pipeline.py +292 -0
  42. planner_lab-0.1.0/src/planner_lab/agents/state.py +18 -0
  43. planner_lab-0.1.0/src/planner_lab/agents/structured.py +46 -0
  44. planner_lab-0.1.0/src/planner_lab/agents/tools.py +238 -0
  45. planner_lab-0.1.0/src/planner_lab/calculators/__init__.py +25 -0
  46. planner_lab-0.1.0/src/planner_lab/calculators/conversions.py +24 -0
  47. planner_lab-0.1.0/src/planner_lab/calculators/fi_timeline.py +56 -0
  48. planner_lab-0.1.0/src/planner_lab/calculators/funded_ratio.py +27 -0
  49. planner_lab-0.1.0/src/planner_lab/calculators/withdrawal.py +28 -0
  50. planner_lab-0.1.0/src/planner_lab/case_io.py +21 -0
  51. planner_lab-0.1.0/src/planner_lab/cli.py +334 -0
  52. planner_lab-0.1.0/src/planner_lab/critic/__init__.py +3 -0
  53. planner_lab-0.1.0/src/planner_lab/critic/checks.py +242 -0
  54. planner_lab-0.1.0/src/planner_lab/critic/run.py +40 -0
  55. planner_lab-0.1.0/src/planner_lab/hooks/__init__.py +3 -0
  56. planner_lab-0.1.0/src/planner_lab/hooks/compliance.py +47 -0
  57. planner_lab-0.1.0/src/planner_lab/memo/__init__.py +4 -0
  58. planner_lab-0.1.0/src/planner_lab/memo/disclaimer.py +9 -0
  59. planner_lab-0.1.0/src/planner_lab/memo/render.py +99 -0
  60. planner_lab-0.1.0/src/planner_lab/protocols.py +65 -0
  61. planner_lab-0.1.0/src/planner_lab/schemas/__init__.py +55 -0
  62. planner_lab-0.1.0/src/planner_lab/schemas/assumptions.py +80 -0
  63. planner_lab-0.1.0/src/planner_lab/schemas/case_file.py +172 -0
  64. planner_lab-0.1.0/src/planner_lab/schemas/critic.py +43 -0
  65. planner_lab-0.1.0/src/planner_lab/schemas/memo.py +68 -0
  66. planner_lab-0.1.0/src/planner_lab/schemas/results.py +156 -0
  67. planner_lab-0.1.0/src/planner_lab/telemetry.py +19 -0
  68. planner_lab-0.1.0/tests/__init__.py +0 -0
  69. planner_lab-0.1.0/tests/data/synthetic_transactions_actual.csv +7 -0
  70. planner_lab-0.1.0/tests/data/synthetic_transactions_monarch.csv +56 -0
  71. planner_lab-0.1.0/tests/data/synthetic_transactions_ynab.csv +9 -0
  72. planner_lab-0.1.0/tests/support/__init__.py +0 -0
  73. planner_lab-0.1.0/tests/support/fake_adapters.py +65 -0
  74. planner_lab-0.1.0/tests/support/fixtures.py +127 -0
  75. planner_lab-0.1.0/tests/support/stub_model.py +66 -0
  76. planner_lab-0.1.0/tests/test_adapter_fundedness.py +172 -0
  77. planner_lab-0.1.0/tests/test_adapter_lifecycle.py +54 -0
  78. planner_lab-0.1.0/tests/test_adapter_mcp_research.py +113 -0
  79. planner_lab-0.1.0/tests/test_adapter_monteplan.py +74 -0
  80. planner_lab-0.1.0/tests/test_adapters_lazy.py +62 -0
  81. planner_lab-0.1.0/tests/test_calculators.py +106 -0
  82. planner_lab-0.1.0/tests/test_cli.py +148 -0
  83. planner_lab-0.1.0/tests/test_critic.py +176 -0
  84. planner_lab-0.1.0/tests/test_csv_import.py +129 -0
  85. planner_lab-0.1.0/tests/test_hooks.py +67 -0
  86. planner_lab-0.1.0/tests/test_memo_render.py +47 -0
  87. planner_lab-0.1.0/tests/test_pipeline_diagnostics.py +77 -0
  88. planner_lab-0.1.0/tests/test_pipeline_research.py +84 -0
  89. planner_lab-0.1.0/tests/test_pipeline_stub.py +130 -0
  90. planner_lab-0.1.0/tests/test_schemas.py +68 -0
  91. planner_lab-0.1.0/tests/test_trace.py +36 -0
  92. planner_lab-0.1.0/uv.lock +3262 -0
@@ -0,0 +1,35 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+ - uses: astral-sh/setup-uv@v8.3.2
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ enable-cache: true
21
+ - run: uv sync --all-extras
22
+ - run: uv run pytest
23
+
24
+ lint:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v5
28
+ - uses: astral-sh/setup-uv@v8.3.2
29
+ with:
30
+ python-version: "3.12"
31
+ enable-cache: true
32
+ - run: uv sync --all-extras
33
+ - run: uv run ruff check .
34
+ - run: uv run ruff format --check .
35
+ - run: uv run mypy
@@ -0,0 +1,21 @@
1
+ name: release
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ pypi-publish:
9
+ name: Upload release to PyPI
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v5
16
+ - uses: astral-sh/setup-uv@v8.3.2
17
+ with:
18
+ python-version: "3.12"
19
+ - run: uv build
20
+ - name: Publish package distributions to PyPI
21
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,33 @@
1
+ # Private working notes — never commit
2
+ *.local.md
3
+ CLAUDE.local.md
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *.egg-info/
9
+ .venv/
10
+ dist/
11
+ build/
12
+
13
+ # Slop-lint venv (auto-created by scripts/slopcheck.sh)
14
+ scripts/.slopvenv/
15
+
16
+ # Tooling caches
17
+ .pytest_cache/
18
+ .mypy_cache/
19
+ .ruff_cache/
20
+ .coverage
21
+ htmlcov/
22
+
23
+ # Environment / secrets
24
+ .env
25
+ .env.*
26
+
27
+ # Real financial data must never land in the repo
28
+ data/private/
29
+ *.ofx
30
+ *.qfx
31
+
32
+ # OS
33
+ .DS_Store
@@ -0,0 +1,16 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ title: "agentic-financial-planner-lab: an auditable financial planning agent framework"
4
+ authors:
5
+ - family-names: Hodge
6
+ given-names: John
7
+ url: "https://github.com/jman4162/agentic-financial-planner-lab"
8
+ license: MIT
9
+ version: 0.1.0
10
+ date-released: 2026-07-10
11
+ keywords:
12
+ - financial-planning
13
+ - retirement
14
+ - monte-carlo
15
+ - llm-agents
16
+ - model-context-protocol
@@ -0,0 +1,90 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ uv sync --extra agent --extra planning --extra dev # full install
9
+ uv run pytest # run tests (offline; live tests are opt-in marks)
10
+ uv run pytest tests/test_x.py::test_y # run a single test
11
+ uv run pytest -m ollama # live tests against local Ollama (excluded by default)
12
+ uv run ruff check . && uv run ruff format --check . # lint/format
13
+ uv run mypy # type check (strict)
14
+ uv run planner-lab validate examples/cases/sample_household.yaml
15
+ uv run planner-lab memo examples/cases/sample_household.yaml -o memo.md --yes # needs Ollama
16
+ uv run planner-lab analyze examples/cases/sample_household.yaml --simulate --health --allocation --yes
17
+ uv run planner-lab import-cashflow examples/data/sample_transactions_monarch.csv --format monarch
18
+ bash scripts/slopcheck.sh # AI-slop lint on public prose (see Writing style)
19
+ ```
20
+
21
+ The package lives in `src/planner_lab/` (src layout, hatchling). Dependencies are grouped as optional extras in `pyproject.toml` (`agent`, `planning`, `portfolio`, `mcp`, `dev`); the core must install, import, and pass its tests with no extras (pipeline/hook tests need `agent`; `test_adapter_*` files skip when their package is absent). `--research` needs the `PLANNER_LAB_RESEARCH_MCP_URL` env var; that URL lives only in the environment, never in code or docs.
22
+
23
+ Known environment quirk: this repo lives under `~/Documents`, and macOS file syncing sometimes marks new `.venv` files with the BSD `hidden` flag, which makes Python skip the editable-install `.pth` file (`ModuleNotFoundError: planner_lab`). Fix: `chflags -R nohidden .venv` and delete any `*.pth` duplicates with ` 2`/` 3` suffixes in `site-packages`.
24
+
25
+ ## Project status
26
+
27
+ Phases 1-6 built: schemas, calculators, traceability ledger, deterministic critic (nine checks), memo renderer, CLI (`validate`/`calc`/`analyze`/`memo`/`intake`/`import-cashflow`), agent pipeline with exactly two LLM call sites (memo writer, LLM critic), compliance hooks, Monte Carlo simulation adapter, generic MCP research adapter with citation enforcement, CEFR health metric, lifecycle allocation diagnostics (framed as comparisons, never instructions — `check_diagnostic_framing` guards this), and a stdlib CSV cash-flow importer with presets for common budgeting-app exports.
28
+
29
+ ## What this project is
30
+
31
+ An experimental, provider-neutral Python framework for auditable personal-finance planning agents: it turns a user's question into a structured household case file, runs deterministic calculators and simulations against it, passes results through a critic/verifier, and emits a planning memo with explicit assumptions, citations, and an educational disclaimer.
32
+
33
+ It is not a financial advisor. It does not execute trades, give individualized securities advice ("buy/sell X"), or pick stocks.
34
+
35
+ ## Core design rules
36
+
37
+ These are architectural invariants, not suggestions:
38
+
39
+ 1. **The LLM never does math.** LLMs handle orchestration, question-asking, tool selection, and explanation. All arithmetic (simulations, tax math, withdrawal rates, funded ratios, portfolio metrics) comes from deterministic, seeded, testable functions the agent calls as tools.
40
+ 2. **Everything flows through a typed case file.** Household facts, goals, balance sheet, cash flow, portfolio, assumptions, and constraints live in a Pydantic model, not loose chat history. Every number in an output memo must be traceable to a case-file input or a calculator result.
41
+ 3. **Assumptions are explicit.** The assumption builder produces labeled base / conservative / optimistic sets and surfaces them before simulations run. Never silently pick optimistic assumptions.
42
+ 4. **A critic/verifier stage is mandatory** before any memo is emitted: numbers traceable, citations present, no individualized securities advice, no nominal/real confusion, certainty not overstated, missing material inputs flagged.
43
+ 5. **Integrations are optional adapters behind generic Protocol interfaces** (`ScenarioSimulator`, `ResearchSource`, `FinancialHealthMetric`, `CashflowImporter`, `PortfolioAnalyticsEngine`). Core workflow must run with no adapter installed; no specific commercial product or third-party account is ever required, and no adapter's name leaks into core classes, schemas, prompts, or diagrams.
44
+ 6. **Examples and docs use synthetic households only.** Never commit real financial data.
45
+
46
+ ## Intended architecture
47
+
48
+ ```
49
+ User question
50
+ → Intake (classify task, ask high-value missing-field questions)
51
+ → Case file builder (typed Pydantic model)
52
+ → Optional data importers (generic CSV first; budgeting exports, ledgers as adapters)
53
+ → Assumption builder (base / conservative / optimistic)
54
+ → Deterministic engines (Monte Carlo simulation, tax models, portfolio diagnostics)
55
+ → Optional research/citation sources (MCP-based, read-only)
56
+ → Critic / verifier
57
+ → Cited planning memo (exec summary, inputs, missing data, base + stress results,
58
+ risks, trade-offs, next questions, methodology, disclaimer)
59
+ ```
60
+
61
+ Agent topology: single orchestrator with specialist sub-agents (intake, ingestion, assumptions, simulation, research, tax/account location, portfolio risk, critic, report writer), not a decentralized swarm.
62
+
63
+ Planned stack: Strands Agents SDK for the agent layer (swap-friendly interface in case LangGraph is needed later for durable/resumable state), OpenTelemetry for tracing, Typer/Rich for CLI, optional local-first LLM via Ollama. Dependencies are grouped as optional extras in `pyproject.toml` (e.g. `agent`, `mcp`, `planning`, `imports`, `market-data`, `portfolio`, `dev`) so the core installs lean.
64
+
65
+ Planned dev tooling: pytest + hypothesis for tests, ruff for lint/format, mypy for types. Simulation code must be deterministic under a fixed seed so results are regression-testable.
66
+
67
+ ## Scope guardrails
68
+
69
+ - Portfolio analysis addresses structure (allocation mix, concentration, fees, factor tilts, sequence risk). It never covers security selection or trade timing.
70
+ - Optimization libraries (mean-variance etc.) are diagnostics and educational examples, not recommendation engines.
71
+ - Market-data integrations fetch neutral series (asset-class returns, yields, inflation), never stock screens.
72
+ - Prefer user-controlled file imports (CSV, plain-text ledgers) over live account aggregation.
73
+
74
+ ## Writing style for public-facing prose
75
+
76
+ Applies to README, docs, docstrings, example output, commit messages, and PR text.
77
+
78
+ - **Required pre-publish slop check.** Before committing new or substantially rewritten public prose, run `bash scripts/slopcheck.sh` (or pass explicit files). It runs two local, no-network linters: `slopscore-lint` (0-100 SlopScore with evidence spans) and `slopless` (rule findings). Both flag patterns, not authorship. Review findings by hand; do not auto-apply. Fix true positives, ignore deliberate house style. Advisory by default; `--strict` exits non-zero on high-severity findings for CI. The script auto-creates a venv at `scripts/.slopvenv` (gitignored) and never scans `*.local.md`.
79
+ - **No em dashes** in public prose. Use commas, periods, or semicolons.
80
+ - **No corrective-contrast framing.** Avoid "X is not Y, it is Z" / "It's not just X, it's Y" couplets and "X, not Y" section headings. State the strong version directly; the strawman rarely adds value.
81
+ - **No puffery or significance narration.** State results; do not narrate their importance ("this is powerful because...", "crucially...").
82
+ - **No formulaic-preamble emphasizers.** No "Importantly:", "Notably:", "At its core...", "Ultimately..." as sentence openers.
83
+ - **No meta-narration.** No "This guide covers..." / "We'll explore..."; readers can see the headings.
84
+ - **No hyperbolic insight-claiming.** No "Here's the thing..." / "What most people miss..." setups.
85
+ - **Avoid reflexive AI vocabulary:** delve, crucial, pivotal, robust, seamless, leverage, showcase, underscore, tapestry, vibrant. Prefer specific, concrete, falsifiable wording.
86
+ - Reference: [Wikipedia: Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing).
87
+
88
+ ## Local files
89
+
90
+ Files matching `*.local.md` are private working notes and are gitignored. Never commit them or copy their contents into committed files. Read `background-info.local.md` for the full design discussion behind this document.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 John Hodge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: planner-lab
3
+ Version: 0.1.0
4
+ Summary: Experimental, provider-neutral framework for auditable personal-finance planning agents
5
+ Author: John Hodge
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: financial-planning,fire,llm-agents,mcp,monte-carlo,personal-finance,retirement
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: pydantic>=2.0
11
+ Requires-Dist: pyyaml>=6.0
12
+ Requires-Dist: rich>=13.0
13
+ Requires-Dist: typer>=0.12
14
+ Provides-Extra: agent
15
+ Requires-Dist: strands-agents-tools>=0.2; extra == 'agent'
16
+ Requires-Dist: strands-agents[ollama,otel]<2,>=1.46; extra == 'agent'
17
+ Provides-Extra: dev
18
+ Requires-Dist: hypothesis>=6.0; extra == 'dev'
19
+ Requires-Dist: mypy>=1.10; extra == 'dev'
20
+ Requires-Dist: pytest>=8.0; extra == 'dev'
21
+ Requires-Dist: ruff>=0.6; extra == 'dev'
22
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
23
+ Provides-Extra: mcp
24
+ Requires-Dist: httpx>=0.27; extra == 'mcp'
25
+ Requires-Dist: mcp>=1.28; extra == 'mcp'
26
+ Provides-Extra: planning
27
+ Requires-Dist: fundedness>=0.2.4; extra == 'planning'
28
+ Requires-Dist: monteplan>=0.6; extra == 'planning'
29
+ Provides-Extra: portfolio
30
+ Requires-Dist: lifecycle-allocation>=0.2; extra == 'portfolio'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # agentic-financial-planner-lab
34
+
35
+ [![ci](https://github.com/jman4162/agentic-financial-planner-lab/actions/workflows/ci.yml/badge.svg)](https://github.com/jman4162/agentic-financial-planner-lab/actions/workflows/ci.yml)
36
+ [![license](https://img.shields.io/github/license/jman4162/agentic-financial-planner-lab)](LICENSE)
37
+
38
+ An experimental, provider-neutral framework for building auditable personal-finance planning agents: a typed case file, deterministic calculators, a Monte Carlo simulation adapter, cited research through the Model Context Protocol, and a critic gate that blocks any memo whose numbers cannot be traced to a recorded computation.
39
+
40
+ **The LLM never does the math.** Every dollar figure in an output memo resolves to a recorded calculator run or a case-file field, and a critic rejects the memo otherwise. This is a research and education project: not a financial advisor, no trade execution, no stock picking, no required account anywhere. Examples use synthetic households only (see the [disclaimer](#license)).
41
+
42
+ ![Demo of validate, calc, and import-cashflow commands](docs/assets/demo.gif)
43
+
44
+ **Keywords:** financial planning, retirement readiness, Monte Carlo simulation, LLM agents, Model Context Protocol (MCP), funded ratio, safe withdrawal rate, personal finance, FIRE.
45
+
46
+ ## Architecture
47
+
48
+ The LLM appears at exactly two points (memo drafting and a tone review); everything else is deterministic, seeded, and testable.
49
+
50
+ ```mermaid
51
+ flowchart TD
52
+ A[Case file: typed household facts] --> B[Assumptions gate:<br/>base / conservative / optimistic,<br/>confirmed by the user]
53
+ B --> C[Deterministic engines]
54
+ C --> C1[Calculators:<br/>funded ratio, FI timeline]
55
+ C --> C2[Monte Carlo simulation]
56
+ C --> C3[CEFR health metric]
57
+ C --> C4[Allocation diagnostics]
58
+ C1 --> D[(Computation ledger:<br/>every result gets a citable id)]
59
+ C2 --> D
60
+ C3 --> D
61
+ C4 --> D
62
+ E[MCP research source:<br/>search + fetch, recorded] --> D
63
+ D --> F[Memo writer LLM:<br/>drafts prose, cites only<br/>ids from the ledger menu]
64
+ F --> G{Critic gate:<br/>9 deterministic checks<br/>+ LLM tone review}
65
+ G -- approved --> H[Planning memo<br/>+ audit sidecar]
66
+ G -- rejected twice --> I[Hard failure:<br/>no memo emitted]
67
+ ```
68
+
69
+ ## How it compares
70
+
71
+ | | Spreadsheet / DIY | Web retirement simulators | Generic LLM chat | This project |
72
+ |---|---|---|---|---|
73
+ | Deterministic, seeded math | yes | yes | no | yes |
74
+ | Explains trade-offs in prose | no | limited | yes | yes |
75
+ | Every number traceable to a computation | manual | no | no | enforced by a critic |
76
+ | Cited methodology sources | manual | some | fabrication risk | fetched and verified |
77
+ | Runs fully local | yes | no | rarely | yes (Ollama) |
78
+ | Refuses unverifiable output | n/a | n/a | no | yes |
79
+
80
+ ## Install
81
+
82
+ Requires Python 3.11+.
83
+
84
+ ```bash
85
+ uv sync --extra agent --extra dev
86
+ ```
87
+
88
+ Or with pip:
89
+
90
+ ```bash
91
+ pip install -e ".[agent,dev]"
92
+ ```
93
+
94
+ Extras are optional by design. The core installs without any agent framework; `agent` adds the Strands SDK (with Ollama and OpenTelemetry support), `mcp` adds Model Context Protocol clients for research sources.
95
+
96
+ ## Try it
97
+
98
+ The core works offline with no LLM:
99
+
100
+ ```bash
101
+ uv run planner-lab validate examples/cases/sample_household.yaml
102
+ uv run planner-lab calc funded-ratio --portfolio 900000 --spending 50000
103
+ uv run planner-lab calc fi-timeline --portfolio 250000 --savings 50000 --spending 60000
104
+ ```
105
+
106
+ With a local [Ollama](https://ollama.com/) server running and a tool-calling-capable model pulled (default `qwen3`; override with `OLLAMA_MODEL`):
107
+
108
+ ```bash
109
+ # Full pipeline: assumptions -> calculators -> memo draft -> critic gate -> memo
110
+ uv run planner-lab memo examples/cases/sample_household.yaml -o memo.md --yes
111
+
112
+ # Add Monte Carlo simulation, the fundedness metric, and allocation diagnostics
113
+ uv sync --all-extras
114
+ uv run planner-lab analyze examples/cases/sample_household.yaml --simulate --health --allocation --yes
115
+
116
+ # Ground the memo's methodology in cited guides from an MCP research server
117
+ PLANNER_LAB_RESEARCH_MCP_URL=https://example.com/mcp \
118
+ uv run planner-lab memo examples/cases/sample_household.yaml -o memo.md --yes --research
119
+
120
+ # Derive annual cash flow from a budgeting-app CSV export (no LLM involved)
121
+ uv run planner-lab import-cashflow examples/data/sample_transactions_monarch.csv \
122
+ --format monarch --case my_case.yaml --write
123
+
124
+ # Interactive intake chat that builds a case file
125
+ uv run planner-lab intake -o my_case.yaml
126
+
127
+ # Minimal agent-plus-tool example
128
+ uv run python examples/hello_agent.py
129
+ ```
130
+
131
+ The memo command writes the markdown memo plus a `.audit.json` sidecar holding the full computation ledger and critic report, so every number can be checked by hand. If the critic rejects the draft twice, no memo is written and the failing checks are printed.
132
+
133
+ ## Case studies
134
+
135
+ Full walkthroughs with real generated memos, checked in verbatim:
136
+
137
+ - [An on-track couple, full analysis](docs/case-studies/on-track-couple.md): simulation percentiles, the CEFR health metric, allocation diagnostics, and cited research in one run.
138
+ - [Data gaps and CSV import](docs/case-studies/data-gaps-and-csv-import.md): missing data is disclosed, then partially filled from a transactions export with no LLM involved.
139
+ - [A rejected memo](docs/case-studies/rejected-memo.md): what the critic gate catches, and why the worst case is no memo rather than a wrong one.
140
+
141
+ ## How a run works
142
+
143
+ 1. The case file is loaded and validated; material gaps are recorded.
144
+ 2. Base, conservative, and optimistic assumption sets are shown for confirmation (`--yes` accepts them non-interactively). Rates are real, after inflation.
145
+ 3. Deterministic calculators run per assumption set: funded ratio, years to financial independence, sustainable spending. Each result lands in the computation ledger with an id.
146
+ 4. Optionally, a Monte Carlo engine simulates each set (plus crash and sequence-risk stress runs) behind a generic `ScenarioSimulator` interface.
147
+ 5. The LLM drafts the memo, citing numbers only from the ledger menu it is given.
148
+ 6. The critic runs eight deterministic checks (traceability, no securities advice, disclaimer, assumption disclosure, missing-data disclosure, citation consistency, real/nominal labeling, certainty language) plus an LLM tone review. One revision is attempted; then the run fails.
149
+
150
+ ## Configuration
151
+
152
+ Everything is configured through environment variables; nothing is hardcoded.
153
+
154
+ | Variable | Purpose | Default |
155
+ |---|---|---|
156
+ | `PLANNER_LAB_MODEL_PROVIDER` | `ollama` or `bedrock` | `ollama` |
157
+ | `OLLAMA_HOST` / `OLLAMA_MODEL` | Local model server and model id | `http://localhost:11434` / `qwen3` |
158
+ | `PLANNER_LAB_BEDROCK_MODEL` | Bedrock model id (provider `bedrock`) | provider default |
159
+ | `PLANNER_LAB_RESEARCH_MCP_URL` | Streamable-HTTP MCP server exposing `search` and `fetch` tools; enables `--research` | unset |
160
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` / `OTEL_EXPORTER_OTLP_HEADERS` | OTLP tracing target for `setup_telemetry("otlp")`; `--trace` prints spans to stdout | unset |
161
+
162
+ Small local models occasionally mis-copy a number or skip a citation; the critic then rejects the memo after one revision attempt rather than emitting it. A larger model (for example `OLLAMA_MODEL=gpt-oss:20b`) makes full runs with simulation, diagnostics, and research more reliable.
163
+
164
+ Optional extras: `agent` (LLM pipeline, Strands SDK), `planning` (Monte Carlo simulation, fundedness metric), `portfolio` (lifecycle allocation diagnostics), `mcp` (research sources), `dev` (tests, lint, types). The core installs with none of them.
165
+
166
+ ## Status
167
+
168
+ Working retirement-readiness pipeline: case-file schemas, deterministic calculators, critic gate, memo renderer, interactive intake, Monte Carlo simulation, certainty-equivalent funded ratio (CEFR) metric, lifecycle allocation diagnostics, MCP research citations, and CSV cash-flow import. See `CLAUDE.md` for architecture rules.
169
+
170
+ ## Citation
171
+
172
+ ```bibtex
173
+ @software{hodge_agentic_financial_planner_lab,
174
+ author = {Hodge, John},
175
+ title = {agentic-financial-planner-lab: an auditable financial planning agent framework},
176
+ year = {2026},
177
+ url = {https://github.com/jman4162/agentic-financial-planner-lab},
178
+ version = {0.1.0},
179
+ license = {MIT}
180
+ }
181
+ ```
182
+
183
+ ## License
184
+
185
+ MIT. Educational use; nothing here is financial advice.
@@ -0,0 +1,153 @@
1
+ # agentic-financial-planner-lab
2
+
3
+ [![ci](https://github.com/jman4162/agentic-financial-planner-lab/actions/workflows/ci.yml/badge.svg)](https://github.com/jman4162/agentic-financial-planner-lab/actions/workflows/ci.yml)
4
+ [![license](https://img.shields.io/github/license/jman4162/agentic-financial-planner-lab)](LICENSE)
5
+
6
+ An experimental, provider-neutral framework for building auditable personal-finance planning agents: a typed case file, deterministic calculators, a Monte Carlo simulation adapter, cited research through the Model Context Protocol, and a critic gate that blocks any memo whose numbers cannot be traced to a recorded computation.
7
+
8
+ **The LLM never does the math.** Every dollar figure in an output memo resolves to a recorded calculator run or a case-file field, and a critic rejects the memo otherwise. This is a research and education project: not a financial advisor, no trade execution, no stock picking, no required account anywhere. Examples use synthetic households only (see the [disclaimer](#license)).
9
+
10
+ ![Demo of validate, calc, and import-cashflow commands](docs/assets/demo.gif)
11
+
12
+ **Keywords:** financial planning, retirement readiness, Monte Carlo simulation, LLM agents, Model Context Protocol (MCP), funded ratio, safe withdrawal rate, personal finance, FIRE.
13
+
14
+ ## Architecture
15
+
16
+ The LLM appears at exactly two points (memo drafting and a tone review); everything else is deterministic, seeded, and testable.
17
+
18
+ ```mermaid
19
+ flowchart TD
20
+ A[Case file: typed household facts] --> B[Assumptions gate:<br/>base / conservative / optimistic,<br/>confirmed by the user]
21
+ B --> C[Deterministic engines]
22
+ C --> C1[Calculators:<br/>funded ratio, FI timeline]
23
+ C --> C2[Monte Carlo simulation]
24
+ C --> C3[CEFR health metric]
25
+ C --> C4[Allocation diagnostics]
26
+ C1 --> D[(Computation ledger:<br/>every result gets a citable id)]
27
+ C2 --> D
28
+ C3 --> D
29
+ C4 --> D
30
+ E[MCP research source:<br/>search + fetch, recorded] --> D
31
+ D --> F[Memo writer LLM:<br/>drafts prose, cites only<br/>ids from the ledger menu]
32
+ F --> G{Critic gate:<br/>9 deterministic checks<br/>+ LLM tone review}
33
+ G -- approved --> H[Planning memo<br/>+ audit sidecar]
34
+ G -- rejected twice --> I[Hard failure:<br/>no memo emitted]
35
+ ```
36
+
37
+ ## How it compares
38
+
39
+ | | Spreadsheet / DIY | Web retirement simulators | Generic LLM chat | This project |
40
+ |---|---|---|---|---|
41
+ | Deterministic, seeded math | yes | yes | no | yes |
42
+ | Explains trade-offs in prose | no | limited | yes | yes |
43
+ | Every number traceable to a computation | manual | no | no | enforced by a critic |
44
+ | Cited methodology sources | manual | some | fabrication risk | fetched and verified |
45
+ | Runs fully local | yes | no | rarely | yes (Ollama) |
46
+ | Refuses unverifiable output | n/a | n/a | no | yes |
47
+
48
+ ## Install
49
+
50
+ Requires Python 3.11+.
51
+
52
+ ```bash
53
+ uv sync --extra agent --extra dev
54
+ ```
55
+
56
+ Or with pip:
57
+
58
+ ```bash
59
+ pip install -e ".[agent,dev]"
60
+ ```
61
+
62
+ Extras are optional by design. The core installs without any agent framework; `agent` adds the Strands SDK (with Ollama and OpenTelemetry support), `mcp` adds Model Context Protocol clients for research sources.
63
+
64
+ ## Try it
65
+
66
+ The core works offline with no LLM:
67
+
68
+ ```bash
69
+ uv run planner-lab validate examples/cases/sample_household.yaml
70
+ uv run planner-lab calc funded-ratio --portfolio 900000 --spending 50000
71
+ uv run planner-lab calc fi-timeline --portfolio 250000 --savings 50000 --spending 60000
72
+ ```
73
+
74
+ With a local [Ollama](https://ollama.com/) server running and a tool-calling-capable model pulled (default `qwen3`; override with `OLLAMA_MODEL`):
75
+
76
+ ```bash
77
+ # Full pipeline: assumptions -> calculators -> memo draft -> critic gate -> memo
78
+ uv run planner-lab memo examples/cases/sample_household.yaml -o memo.md --yes
79
+
80
+ # Add Monte Carlo simulation, the fundedness metric, and allocation diagnostics
81
+ uv sync --all-extras
82
+ uv run planner-lab analyze examples/cases/sample_household.yaml --simulate --health --allocation --yes
83
+
84
+ # Ground the memo's methodology in cited guides from an MCP research server
85
+ PLANNER_LAB_RESEARCH_MCP_URL=https://example.com/mcp \
86
+ uv run planner-lab memo examples/cases/sample_household.yaml -o memo.md --yes --research
87
+
88
+ # Derive annual cash flow from a budgeting-app CSV export (no LLM involved)
89
+ uv run planner-lab import-cashflow examples/data/sample_transactions_monarch.csv \
90
+ --format monarch --case my_case.yaml --write
91
+
92
+ # Interactive intake chat that builds a case file
93
+ uv run planner-lab intake -o my_case.yaml
94
+
95
+ # Minimal agent-plus-tool example
96
+ uv run python examples/hello_agent.py
97
+ ```
98
+
99
+ The memo command writes the markdown memo plus a `.audit.json` sidecar holding the full computation ledger and critic report, so every number can be checked by hand. If the critic rejects the draft twice, no memo is written and the failing checks are printed.
100
+
101
+ ## Case studies
102
+
103
+ Full walkthroughs with real generated memos, checked in verbatim:
104
+
105
+ - [An on-track couple, full analysis](docs/case-studies/on-track-couple.md): simulation percentiles, the CEFR health metric, allocation diagnostics, and cited research in one run.
106
+ - [Data gaps and CSV import](docs/case-studies/data-gaps-and-csv-import.md): missing data is disclosed, then partially filled from a transactions export with no LLM involved.
107
+ - [A rejected memo](docs/case-studies/rejected-memo.md): what the critic gate catches, and why the worst case is no memo rather than a wrong one.
108
+
109
+ ## How a run works
110
+
111
+ 1. The case file is loaded and validated; material gaps are recorded.
112
+ 2. Base, conservative, and optimistic assumption sets are shown for confirmation (`--yes` accepts them non-interactively). Rates are real, after inflation.
113
+ 3. Deterministic calculators run per assumption set: funded ratio, years to financial independence, sustainable spending. Each result lands in the computation ledger with an id.
114
+ 4. Optionally, a Monte Carlo engine simulates each set (plus crash and sequence-risk stress runs) behind a generic `ScenarioSimulator` interface.
115
+ 5. The LLM drafts the memo, citing numbers only from the ledger menu it is given.
116
+ 6. The critic runs eight deterministic checks (traceability, no securities advice, disclaimer, assumption disclosure, missing-data disclosure, citation consistency, real/nominal labeling, certainty language) plus an LLM tone review. One revision is attempted; then the run fails.
117
+
118
+ ## Configuration
119
+
120
+ Everything is configured through environment variables; nothing is hardcoded.
121
+
122
+ | Variable | Purpose | Default |
123
+ |---|---|---|
124
+ | `PLANNER_LAB_MODEL_PROVIDER` | `ollama` or `bedrock` | `ollama` |
125
+ | `OLLAMA_HOST` / `OLLAMA_MODEL` | Local model server and model id | `http://localhost:11434` / `qwen3` |
126
+ | `PLANNER_LAB_BEDROCK_MODEL` | Bedrock model id (provider `bedrock`) | provider default |
127
+ | `PLANNER_LAB_RESEARCH_MCP_URL` | Streamable-HTTP MCP server exposing `search` and `fetch` tools; enables `--research` | unset |
128
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` / `OTEL_EXPORTER_OTLP_HEADERS` | OTLP tracing target for `setup_telemetry("otlp")`; `--trace` prints spans to stdout | unset |
129
+
130
+ Small local models occasionally mis-copy a number or skip a citation; the critic then rejects the memo after one revision attempt rather than emitting it. A larger model (for example `OLLAMA_MODEL=gpt-oss:20b`) makes full runs with simulation, diagnostics, and research more reliable.
131
+
132
+ Optional extras: `agent` (LLM pipeline, Strands SDK), `planning` (Monte Carlo simulation, fundedness metric), `portfolio` (lifecycle allocation diagnostics), `mcp` (research sources), `dev` (tests, lint, types). The core installs with none of them.
133
+
134
+ ## Status
135
+
136
+ Working retirement-readiness pipeline: case-file schemas, deterministic calculators, critic gate, memo renderer, interactive intake, Monte Carlo simulation, certainty-equivalent funded ratio (CEFR) metric, lifecycle allocation diagnostics, MCP research citations, and CSV cash-flow import. See `CLAUDE.md` for architecture rules.
137
+
138
+ ## Citation
139
+
140
+ ```bibtex
141
+ @software{hodge_agentic_financial_planner_lab,
142
+ author = {Hodge, John},
143
+ title = {agentic-financial-planner-lab: an auditable financial planning agent framework},
144
+ year = {2026},
145
+ url = {https://github.com/jman4162/agentic-financial-planner-lab},
146
+ version = {0.1.0},
147
+ license = {MIT}
148
+ }
149
+ ```
150
+
151
+ ## License
152
+
153
+ MIT. Educational use; nothing here is financial advice.
@@ -0,0 +1,8 @@
1
+ # Documentation
2
+
3
+ - [Case studies](case-studies/): full walkthroughs with real generated memos
4
+ - [An on-track couple, full analysis](case-studies/on-track-couple.md): simulation, health metric, allocation diagnostics, and cited research in one run
5
+ - [Data gaps and CSV import](case-studies/data-gaps-and-csv-import.md): how missing data is disclosed and filled from a transactions export
6
+ - [A rejected memo](case-studies/rejected-memo.md): what the critic gate catches and why failing loudly is the point
7
+
8
+ For architecture rules and development commands, see [CLAUDE.md](../CLAUDE.md) and the main [README](../README.md).
Binary file
@@ -0,0 +1,60 @@
1
+ # Case study: data gaps and CSV import
2
+
3
+ A synthetic 38-year-old asks *"Am I on track for retirement?"* with a thin case file, [`examples/cases/incomplete_household.yaml`](../../examples/cases/incomplete_household.yaml): one pre-tax account, a gross income, and nothing else. This walkthrough shows how the system refuses to paper over what it does not know, and how a transactions CSV fills part of the gap without an LLM anywhere in the loop.
4
+
5
+ ## Step 1: validation names the gaps
6
+
7
+ ```text
8
+ $ planner-lab validate riley_case.yaml
9
+ Valid case file: incomplete-household
10
+ Question: Am I on track for retirement?
11
+ Net worth: $95,000
12
+ Investable assets: $95,000
13
+ Missing material fields:
14
+ - cash_flow.annual_expenses
15
+ - cash_flow.annual_savings
16
+ - goals.retirement.annual_amount_today
17
+ - portfolio
18
+ ```
19
+
20
+ These dotted paths follow the case everywhere. The critic gate later requires every one of them to appear in the memo's "Important missing data" section.
21
+
22
+ ## Step 2: derive cash flow from a transactions export
23
+
24
+ ```text
25
+ $ planner-lab import-cashflow examples/data/sample_transactions_monarch.csv \
26
+ --format monarch --case riley_case.yaml --write
27
+
28
+ Cash flow derived from sample_transactions_monarch.csv
29
+ ┌────────────────────┬──────────────────────────────────────┐
30
+ │ Window │ 2025-02-01 to 2026-02-01 (exclusive) │
31
+ │ Complete months │ 12 │
32
+ │ Income (take-home) │ $96,000/yr │
33
+ │ Expenses │ $44,949/yr │
34
+ │ Savings │ $51,051/yr │
35
+ │ Excluded transfers │ $48,000/yr │
36
+ └────────────────────┴──────────────────────────────────────┘
37
+
38
+ Case file cash flow (current -> imported):
39
+ annual_take_home: unset -> $96,000
40
+ annual_expenses: unset -> $44,949
41
+ annual_savings: unset -> $51,051
42
+ Case file updated: riley_case.yaml
43
+ ```
44
+
45
+ The importer is stdlib code with a column-mapping preset per export format. Note the excluded-transfers line: $48,000 of credit-card payments and brokerage transfers were kept out of income and expenses, because counting them would double-count spending that was already recorded at the merchant level. The window is the last 12 complete calendar months; shorter exports are annualized with an explicit warning.
46
+
47
+ ## Step 3: the remaining gaps stay visible
48
+
49
+ ```text
50
+ $ planner-lab validate riley_case.yaml
51
+ Missing material fields:
52
+ - goals.retirement.annual_amount_today
53
+ - portfolio
54
+ ```
55
+
56
+ The cash-flow gaps cleared because the data now exists; the portfolio and the retirement spending target still need the human. Running the memo pipeline now produces [`data-gaps-memo.md`](data-gaps-memo.md), whose "Important missing data" section carries both remaining paths (the critic blocks the memo if it does not) and whose analysis leans on the expense figure as the spending proxy, labeled as such.
57
+
58
+ ## Why this matters
59
+
60
+ A planning answer computed from unstated guesses is worse than no answer. The pipeline's rule is that gaps are data: they are recorded in the case file, disclosed in the memo, and enforced by a deterministic check rather than by hoping the model mentions them.