Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
a940a10
feat: Report builder
anth-volk Dec 19, 2025
12ee148
feat: Report meta panel
anth-volk Dec 22, 2025
f8c4c2e
fix: Visual improvements to policy setup modal
anth-volk Dec 22, 2025
39eb8dc
fix: Minor visual fixes
anth-volk Dec 22, 2025
9397374
feat: First implementation of existing policy selector modal
anth-volk Dec 22, 2025
114fdb7
feat: View user's existing policy details
anth-volk Dec 23, 2025
0902139
feat: First version of policy creation modal
anth-volk Dec 23, 2025
78edce5
fix: Info panel fixes
anth-volk Dec 24, 2025
2635548
fix: Various fixes to param creator
anth-volk Dec 26, 2025
82279d3
feat: Parameter search
anth-volk Dec 26, 2025
0e8fb43
fix: Filter out pycache params
anth-volk Dec 26, 2025
8c9199e
fix: Use proper policy loading structure
anth-volk Dec 26, 2025
019feb6
fix: Properly use association data for household display
anth-volk Dec 26, 2025
bc931bb
fix: Fallback to Policy #X for policies with no label
anth-volk Dec 29, 2025
41de0eb
fix: Remove horizontal view
anth-volk Dec 29, 2025
256f74a
feat: Usage tracking
anth-volk Dec 29, 2025
c6b5298
feat: Geography utils
anth-volk Feb 12, 2026
36ab6be
feat: PopulationBrowseModal
anth-volk Dec 29, 2025
087b548
feat: Population creation & selection modal
anth-volk Dec 29, 2025
09fb4a0
fix: Remove dupe button
anth-volk Dec 29, 2025
e67c4f7
feat: Move household creation into modal
anth-volk Dec 30, 2025
7512466
fix: Fixes to household creation modal
anth-volk Dec 30, 2025
e1e210e
fix: Remove cancel button from household creation modal
anth-volk Dec 30, 2025
38b00c2
fix: Properly create new policy
anth-volk Dec 31, 2025
049202d
fix: Don't allow single-sim reports for subnational geographies
anth-volk Dec 31, 2025
1a6d24d
chore: Refactor, part 1
anth-volk Dec 31, 2025
a6cd857
chore: Further refactor
anth-volk Dec 31, 2025
170b71f
fix: Report builder modal improvements
anth-volk Jan 5, 2026
7fe2142
fix: Improve US Congressional district formatting
anth-volk Jan 5, 2026
9c18d91
feat: Enable Run button
anth-volk Jan 5, 2026
cf4a713
feat: Add edit icon for names
anth-volk Jan 6, 2026
95eb4f5
fix: Fix param value display
anth-volk Jan 6, 2026
a10b646
feat: Modify policy creation modal
anth-volk Jan 9, 2026
de0f1e7
fix: Remove param demos
anth-volk Feb 13, 2026
137cd7a
fix: Allow nameless policies
anth-volk Feb 13, 2026
cc7a987
fix: Fix dates, US nationwide populations
anth-volk Feb 13, 2026
4bd365d
feat: Builder variants
anth-volk Feb 13, 2026
665c516
feat: Add places
anth-volk Feb 13, 2026
501875a
fix: Move to more streamlined design
anth-volk Feb 17, 2026
c780d97
fix: Modify top bar
anth-volk Feb 17, 2026
a184b9a
fix: Fix simulation label editing
anth-volk Feb 17, 2026
f45b5be
feat: Modified report builder interface
anth-volk Feb 18, 2026
747dd95
feat: Expand use cases that report builder supports
anth-volk Feb 18, 2026
184433c
feat: Report modification screen
anth-volk Feb 19, 2026
179e846
fix: Harmonize competing hydrated report models
anth-volk Feb 19, 2026
c41a90a
feat: Report modify submission, naming modal, Reports page columns, a…
anth-volk Feb 19, 2026
9d7c337
feat: First implementation of a policy modification structure
anth-volk Feb 20, 2026
8bc593d
feat: Policy editor footer layout, report view/edit modes, and UX ref…
anth-volk Feb 20, 2026
3c27f89
fix: Year selector styling, button ordering, update icon, and overflo…
anth-volk Feb 20, 2026
636a12f
fix: Prevent EditableLabel from overflowing simulation card
anth-volk Feb 20, 2026
87a67b1
feat: Clean up policy editing buttons in report builder
anth-volk Feb 20, 2026
2bbb73b
feat: Icon action buttons for tables, remove checkboxes, policy edito…
anth-volk Feb 20, 2026
0b1489f
feat: Standardized action buttons, view/edit split, and consistent ic…
anth-volk Feb 23, 2026
ca18dd9
feat: Simplify table actions and add view policy button
anth-volk Feb 23, 2026
e63fb91
fix: Ensure policy modal respects initialEditorMode on open
anth-volk Feb 23, 2026
cf4d3b8
feat: Replace report creation pathway with report builder, polish pol…
anth-volk Feb 24, 2026
17cc78b
feat: Implement "Update existing policy" in policy modals
anth-volk Feb 24, 2026
e8f6407
fix: Use design tokens, fix title case, extract shared PolicyOverview…
anth-volk Feb 24, 2026
8433121
fix: Consistent ingredient card padding, remove disabled account sett…
anth-volk Feb 25, 2026
83cb12b
fix: Reuse PolicyOverviewContent in policy details drawer
anth-volk Feb 25, 2026
11ec2b1
feat: Swap ingredient colors, default baseline, remove placeholders
anth-volk Feb 25, 2026
3a1e3d2
feat: Enter key submit, unified gear button, back nav, remove loading…
anth-volk Feb 26, 2026
78b1a14
feat: Add Migration tab with collapsible sections, progressive distri…
anth-volk Feb 26, 2026
26759f8
feat: Migrate remaining UK charts, remove mockup page
anth-volk Feb 26, 2026
0c07ffc
feat: Remove tab ribbon, move compute button left of mode selector
anth-volk Feb 26, 2026
a5f3a5b
feat: Restore back breadcrumb, add view/edit policy gear button
anth-volk Feb 26, 2026
ae5ded4
feat: Remove /migration URL, add Overview section, collapse all but O…
anth-volk Feb 26, 2026
4a6fc3b
fix: Move reproduce button to right of view/edit button
anth-volk Feb 26, 2026
3d964f8
feat: Add expandable DashboardCard component, playground page, and re…
anth-volk Feb 27, 2026
4edd775
feat: Fix DashboardCard sizing, add chart height props, and budgetary…
anth-volk Mar 2, 2026
2933750
feat: Add congressional district card, expandable controls row, and f…
anth-volk Mar 3, 2026
807f629
feat: Remove congressional dropdown, fill map to expanded card height
anth-volk Mar 3, 2026
0bd5b6b
feat: Add error state visualization, improve error logging, and UI fixes
anth-volk Mar 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,583 changes: 1,582 additions & 1 deletion app/public/assets/posts/encode-policy-multi-agent-ai/assets/index-BJx1p784.css

Large diffs are not rendered by default.

21,869 changes: 21,833 additions & 36 deletions app/public/assets/posts/encode-policy-multi-agent-ai/assets/index-DC8RyDPP.js

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions app/public/assets/posts/encode-policy-multi-agent-ai/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Automating Tax and Benefit Policy Modeling with Multi-Agent AI</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,600;8..60,700&display=swap" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,600;8..60,700&display=swap"
rel="stylesheet"
/>
<script type="module" crossorigin src="./assets/index-DC8RyDPP.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-BJx1p784.css">
<link rel="stylesheet" crossorigin href="./assets/index-BJx1p784.css" />
</head>
<body>
<div id="root"></div>
Expand Down
31 changes: 26 additions & 5 deletions app/src/CalculatorRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@ import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom';
import PathwayLayout from './components/PathwayLayout';
import StandardLayout from './components/StandardLayout';
import DashboardPage from './pages/Dashboard.page';
import PlaygroundPage from './pages/playground/PlaygroundPage';
import PoliciesPage from './pages/Policies.page';
import PolicyEditingConceptPage from './pages/policyEditingConcepts/PolicyEditingConceptPage';
import PolicyEditingConceptsPage from './pages/policyEditingConcepts/PolicyEditingConcepts.page';
import PopulationsPage from './pages/Populations.page';
import ModifyReportPage from './pages/reportBuilder/ModifyReportPage';
// Old monolithic file preserved but not used - see ./pages/ReportBuilder.page.tsx
import ReportBuilderPage from './pages/reportBuilder/ReportBuilderPage';
import ReportOutputPage from './pages/ReportOutput.page';
import ReportsPage from './pages/Reports.page';
import SimulationsPage from './pages/Simulations.page';
import PolicyPathwayWrapper from './pathways/policy/PolicyPathwayWrapper';
import PopulationPathwayWrapper from './pathways/population/PopulationPathwayWrapper';
import ReportPathwayWrapper from './pathways/report/ReportPathwayWrapper';
import SimulationPathwayWrapper from './pathways/simulation/SimulationPathwayWrapper';
import { CountryGuard } from './routing/guards/CountryGuard';
import { MetadataGuard } from './routing/guards/MetadataGuard';
Expand Down Expand Up @@ -68,10 +73,6 @@ const router = createBrowserRouter(
{
element: <PathwayLayout />,
children: [
{
path: 'reports/create',
element: <ReportPathwayWrapper />,
},
{
path: 'simulations/create',
element: <SimulationPathwayWrapper />,
Expand Down Expand Up @@ -119,6 +120,26 @@ const router = createBrowserRouter(
path: 'policies',
element: <PoliciesPage />,
},
{
path: 'reports/create',
element: <ReportBuilderPage />,
},
{
path: 'reports/create/:userReportId',
element: <ModifyReportPage />,
},
{
path: 'policy-editing-concepts',
element: <PolicyEditingConceptsPage />,
},
{
path: 'policy-editing-concepts/:conceptId',
element: <PolicyEditingConceptPage />,
},
{
path: 'playground',
element: <PlaygroundPage />,
},
{
path: 'account',
element: <div>Account settings page</div>,
Expand Down
14 changes: 10 additions & 4 deletions app/src/api/societyWideCalculation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,18 @@ export async function fetchSocietyWideCalculation(
});

if (!response.ok) {
let body = '';
try {
body = await response.text();
} catch {
// ignore
}
console.error(
'[fetchSocietyWideCalculation] Failed with status:',
response.status,
response.statusText
`[fetchSocietyWideCalculation] ${response.status} ${response.statusText}`,
url,
body
);
throw new Error(`Society-wide calculation failed: ${response.statusText}`);
throw new Error(`Society-wide calculation failed (${response.status}): ${body || response.statusText}`);
}

const data = await response.json();
Expand Down
103 changes: 103 additions & 0 deletions app/src/api/usageTracking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Usage Tracking Store
*
* A lightweight system for tracking "last used" timestamps for any ingredient
* type (policies, households, geographies, etc.).
*
* This is separate from association data - it only tracks when items
* were last accessed, not the items themselves.
*
* Usage:
* import { policyUsageStore } from '@/api/usageTracking';
*
* // Record that a policy was used
* policyUsageStore.recordUsage(policyId);
*
* // Get 5 most recently used policy IDs
* const recentIds = policyUsageStore.getRecentIds(5);
*/

/** ISO timestamp string */
export type UsageData = Record<string, string>;

/**
* Generic store for tracking usage of items by ID.
* Each ingredient type gets its own store instance with a unique storage key.
*/
export class UsageTrackingStore {
constructor(private readonly storageKey: string) {}

/**
* Record that an item was used/accessed.
* Updates the lastUsedAt timestamp.
*/
recordUsage(id: string): string {
const usage = this.getAll();
const timestamp = new Date().toISOString();
usage[id] = timestamp;
localStorage.setItem(this.storageKey, JSON.stringify(usage));
return timestamp;
}

/**
* Get all usage records (id -> lastUsedAt timestamp).
*/
getAll(): UsageData {
try {
const stored = localStorage.getItem(this.storageKey);
return stored ? JSON.parse(stored) : {};
} catch {
console.error(`[UsageTrackingStore] Failed to parse ${this.storageKey}`);
return {};
}
}

/**
* Get IDs sorted by most recently used.
* @param limit Maximum number of IDs to return (default 10)
*/
getRecentIds(limit = 10): string[] {
const usage = this.getAll();
return Object.entries(usage)
.sort(([, a], [, b]) => b.localeCompare(a))
.slice(0, limit)
.map(([id]) => id);
}

/**
* Get the last used timestamp for a specific ID.
*/
getLastUsed(id: string): string | null {
return this.getAll()[id] || null;
}

/**
* Check if an item has any usage recorded.
*/
hasUsage(id: string): boolean {
return !!this.getAll()[id];
}

/**
* Remove usage record for a specific ID.
*/
removeUsage(id: string): void {
const usage = this.getAll();
delete usage[id];
localStorage.setItem(this.storageKey, JSON.stringify(usage));
}

/**
* Clear all usage records for this store.
*/
clear(): void {
localStorage.removeItem(this.storageKey);
}
}

// Pre-configured stores for each ingredient type
export const policyUsageStore = new UsageTrackingStore('policy-usage');
export const householdUsageStore = new UsageTrackingStore('household-usage');
export const geographyUsageStore = new UsageTrackingStore('geography-usage');
export const simulationUsageStore = new UsageTrackingStore('simulation-usage');
export const reportUsageStore = new UsageTrackingStore('report-usage');
73 changes: 13 additions & 60 deletions app/src/components/IngredientReadView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IconPlus } from '@tabler/icons-react';
import { Box, Button, Checkbox, Flex, Loader, Paper, Table, Text, Title } from '@mantine/core';
import { Box, Button, Flex, Loader, Paper, Table, Text, Title } from '@mantine/core';
import { colors, spacing, typography } from '@/designTokens';
import { ColumnConfig, ColumnRenderer, IngredientRecord } from './columns';
import EmptyState from './common/EmptyState';
Expand All @@ -19,9 +19,6 @@ interface IngredientReadViewProps {
searchValue?: string;
onSearchChange?: (value: string) => void;
onMoreFilters?: () => void;
enableSelection?: boolean;
isSelected?: (recordId: string) => boolean;
onSelectionChange?: (recordId: string, selected: boolean) => void;
}

export default function IngredientReadView({
Expand All @@ -38,9 +35,6 @@ export default function IngredientReadView({
searchValue: _searchValue = '',
onSearchChange: _onSearchChange,
onMoreFilters: _onMoreFilters,
enableSelection = true,
isSelected = () => false,
onSelectionChange,
}: IngredientReadViewProps) {
return (
<Box>
Expand Down Expand Up @@ -115,16 +109,6 @@ export default function IngredientReadView({
<Table>
<Table.Thead style={{ backgroundColor: colors.gray[50] }}>
<Table.Tr>
{enableSelection && (
<Table.Th
style={{
width: '48px',
padding: `${spacing.md} ${spacing.lg}`,
}}
>
{/* Optional: Add "select all" checkbox here in the future */}
</Table.Th>
)}
{columns.map((column) => (
<Table.Th
key={column.key}
Expand All @@ -143,49 +127,18 @@ export default function IngredientReadView({
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data.map((record) => {
const selected = isSelected(record.id);
return (
<Table.Tr
key={record.id}
style={{
backgroundColor: selected ? colors.blue[50] : 'transparent',
borderLeft: selected
? `3px solid ${colors.primary[500]}`
: '3px solid transparent',
cursor: enableSelection ? 'pointer' : 'default',
}}
onClick={() => {
if (enableSelection && onSelectionChange) {
onSelectionChange(record.id, !selected);
}
}}
>
{enableSelection && (
<Table.Td style={{ padding: `${spacing.md} ${spacing.lg}` }}>
<Checkbox
checked={selected}
onChange={(event) => {
event.stopPropagation();
if (onSelectionChange) {
onSelectionChange(record.id, event.currentTarget.checked);
}
}}
size="sm"
/>
</Table.Td>
)}
{columns.map((column) => (
<Table.Td
key={column.key}
style={{ padding: `${spacing.md} ${spacing.lg}` }}
>
<ColumnRenderer config={column} record={record} />
</Table.Td>
))}
</Table.Tr>
);
})}
{data.map((record) => (
<Table.Tr key={record.id}>
{columns.map((column) => (
<Table.Td
key={column.key}
style={{ padding: `${spacing.md} ${spacing.lg}` }}
>
<ColumnRenderer config={column} record={record} />
</Table.Td>
))}
</Table.Tr>
))}
</Table.Tbody>
</Table>
)}
Expand Down
9 changes: 2 additions & 7 deletions app/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
IconMail,
IconPlus,
IconScale,
IconSettings2,
IconTestPipe,
IconUsers,
} from '@tabler/icons-react';
import { useLocation, useNavigate } from 'react-router-dom';
Expand All @@ -35,6 +35,7 @@ export default function Sidebar({ isOpen = true }: SidebarProps) {
{ label: 'Simulations', icon: IconGitBranch, path: `/${countryId}/simulations` },
{ label: 'Policies', icon: IconScale, path: `/${countryId}/policies` },
{ label: 'Households', icon: IconUsers, path: `/${countryId}/households` },
{ label: 'Playground', icon: IconTestPipe, path: `/${countryId}/playground` },
];

const resourceItems = [
Expand Down Expand Up @@ -74,12 +75,6 @@ export default function Sidebar({ isOpen = true }: SidebarProps) {
path: 'mailto:hello@policyengine.org',
external: true,
},
{
label: 'Account settings',
icon: IconSettings2,
path: `/${countryId}/account`,
disabled: true,
},
];

if (!isOpen) {
Expand Down
27 changes: 27 additions & 0 deletions app/src/components/columns/ActionsColumn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ActionIcon, Box, Tooltip } from '@mantine/core';
import { spacing } from '@/designTokens';
import { ActionsColumnConfig, IngredientRecord } from './types';

interface ActionsColumnProps {
config: ActionsColumnConfig;
record: IngredientRecord;
}

export function ActionsColumn({ config, record }: ActionsColumnProps) {
return (
<Box style={{ display: 'flex', gap: spacing.xs, justifyContent: 'flex-end' }}>
{config.actions.map((action) => (
<Tooltip key={action.action} label={action.tooltip} position="bottom" withArrow>
<ActionIcon
variant="light"
color={action.color || 'gray'}
size="lg"
onClick={() => config.onAction(action.action, record.id)}
>
{action.icon}
</ActionIcon>
</Tooltip>
))}
</Box>
);
}
Loading