Initial commit
This commit is contained in:
880
skills/documentation/policyengine-design-skill/SKILL.md
Normal file
880
skills/documentation/policyengine-design-skill/SKILL.md
Normal file
@@ -0,0 +1,880 @@
|
||||
---
|
||||
name: policyengine-design
|
||||
description: PolicyEngine visual identity - colors, fonts, logos, and branding for web apps, calculators, charts, and research
|
||||
---
|
||||
|
||||
# PolicyEngine Design System
|
||||
|
||||
PolicyEngine's visual identity and branding guidelines for creating consistent user experiences across web apps, calculators, charts, and research outputs.
|
||||
|
||||
## For Users 👥
|
||||
|
||||
### PolicyEngine Visual Identity
|
||||
|
||||
**Brand colors:**
|
||||
- **Teal** (#39C6C0) - Primary accent color (buttons, highlights, interactive elements)
|
||||
- **Blue** (#2C6496) - Secondary color (links, charts, headers)
|
||||
|
||||
**Typography:**
|
||||
- **Charts:** Roboto Serif
|
||||
- **Web app:** System fonts (sans-serif)
|
||||
- **Streamlit apps:** Default sans-serif
|
||||
|
||||
**Logo:**
|
||||
- Used in charts (bottom right)
|
||||
- Blue version for light backgrounds
|
||||
- White version for dark backgrounds
|
||||
|
||||
### Recognizing PolicyEngine Content
|
||||
|
||||
**You can identify PolicyEngine content by:**
|
||||
- Teal accent color (#39C6C0) on buttons and interactive elements
|
||||
- Blue (#2C6496) in charts and links
|
||||
- Roboto Serif font in charts
|
||||
- PolicyEngine logo in chart footer
|
||||
- Clean, minimal white backgrounds
|
||||
- Data-focused, quantitative presentation
|
||||
|
||||
## For Analysts 📊
|
||||
|
||||
### Chart Branding
|
||||
|
||||
When creating charts for PolicyEngine analysis, follow these guidelines:
|
||||
|
||||
#### Color Palette
|
||||
|
||||
**Primary colors:**
|
||||
```python
|
||||
TEAL_ACCENT = "#39C6C0" # Primary color (teal)
|
||||
BLUE_PRIMARY = "#2C6496" # Secondary color (blue)
|
||||
DARK_GRAY = "#616161" # Text color
|
||||
```
|
||||
|
||||
**Extended palette:**
|
||||
```python
|
||||
# Blues
|
||||
BLUE = "#2C6496"
|
||||
BLUE_LIGHT = "#D8E6F3"
|
||||
BLUE_PRESSED = "#17354F"
|
||||
BLUE_98 = "#F7FAFD"
|
||||
DARK_BLUE_HOVER = "#1d3e5e"
|
||||
DARKEST_BLUE = "#0C1A27"
|
||||
|
||||
# Teals
|
||||
TEAL_ACCENT = "#39C6C0"
|
||||
TEAL_LIGHT = "#F7FDFC"
|
||||
TEAL_PRESSED = "#227773"
|
||||
|
||||
# Grays
|
||||
DARK_GRAY = "#616161"
|
||||
GRAY = "#808080"
|
||||
MEDIUM_LIGHT_GRAY = "#BDBDBD"
|
||||
MEDIUM_DARK_GRAY = "#D2D2D2"
|
||||
LIGHT_GRAY = "#F2F2F2"
|
||||
|
||||
# Accents
|
||||
WHITE = "#FFFFFF"
|
||||
BLACK = "#000000"
|
||||
DARK_RED = "#b50d0d" # For negative values
|
||||
```
|
||||
|
||||
**See current colors:**
|
||||
```bash
|
||||
cat policyengine-app/src/style/colors.js
|
||||
```
|
||||
|
||||
#### Plotly Chart Formatting
|
||||
|
||||
**Standard PolicyEngine chart:**
|
||||
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
|
||||
def format_fig(fig):
|
||||
"""Format chart with PolicyEngine branding."""
|
||||
fig.update_layout(
|
||||
# Typography
|
||||
font=dict(
|
||||
family="Roboto Serif",
|
||||
color="black"
|
||||
),
|
||||
|
||||
# Background
|
||||
plot_bgcolor="white",
|
||||
template="plotly_white",
|
||||
|
||||
# Margins (leave room for logo)
|
||||
margin=dict(
|
||||
l=50,
|
||||
r=100,
|
||||
t=50,
|
||||
b=120,
|
||||
pad=4
|
||||
),
|
||||
|
||||
# Chart size
|
||||
height=600,
|
||||
width=800,
|
||||
)
|
||||
|
||||
# Add PolicyEngine logo (bottom right)
|
||||
fig.add_layout_image(
|
||||
dict(
|
||||
source="https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png",
|
||||
xref="paper",
|
||||
yref="paper",
|
||||
x=1.0,
|
||||
y=-0.10,
|
||||
sizex=0.10,
|
||||
sizey=0.10,
|
||||
xanchor="right",
|
||||
yanchor="bottom"
|
||||
)
|
||||
)
|
||||
|
||||
# Clean modebar
|
||||
fig.update_layout(
|
||||
modebar=dict(
|
||||
bgcolor="rgba(0,0,0,0)",
|
||||
color="rgba(0,0,0,0)"
|
||||
)
|
||||
)
|
||||
|
||||
return fig
|
||||
|
||||
# Usage
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Scatter(x=x_data, y=y_data, line=dict(color=TEAL_ACCENT)))
|
||||
fig = format_fig(fig)
|
||||
```
|
||||
|
||||
**Current implementation:**
|
||||
```bash
|
||||
# See format_fig in action
|
||||
cat givecalc/ui/visualization.py
|
||||
cat policyengine-app/src/pages/policy/output/...
|
||||
```
|
||||
|
||||
#### Chart Colors
|
||||
|
||||
**For line charts:**
|
||||
- Primary line: Teal (#39C6C0) or Blue (#2C6496)
|
||||
- Background lines: Light gray (rgb(180, 180, 180))
|
||||
- Markers: Teal with 70% opacity
|
||||
|
||||
**For bar charts:**
|
||||
- Positive values: Teal (#39C6C0)
|
||||
- Negative values: Dark red (#b50d0d)
|
||||
- Neutral: Gray
|
||||
|
||||
**For multiple series:**
|
||||
Use variations of blue and teal, or discrete color scale:
|
||||
```python
|
||||
colors = ["#2C6496", "#39C6C0", "#17354F", "#227773"]
|
||||
```
|
||||
|
||||
#### Typography
|
||||
|
||||
**Charts:**
|
||||
```python
|
||||
font=dict(family="Roboto Serif", size=14, color="black")
|
||||
```
|
||||
|
||||
**Axis labels:**
|
||||
```python
|
||||
xaxis=dict(
|
||||
title=dict(text="Label", font=dict(size=14)),
|
||||
tickfont=dict(size=12)
|
||||
)
|
||||
```
|
||||
|
||||
**Load Roboto font:**
|
||||
```python
|
||||
# In Streamlit apps
|
||||
st.markdown("""
|
||||
<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=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
""", unsafe_allow_html=True)
|
||||
```
|
||||
|
||||
### Streamlit App Branding
|
||||
|
||||
**Streamlit configuration (.streamlit/config.toml):**
|
||||
|
||||
```toml
|
||||
[theme]
|
||||
base = "light"
|
||||
primaryColor = "#39C6C0" # Teal accent
|
||||
backgroundColor = "#FFFFFF" # White background
|
||||
secondaryBackgroundColor = "#F7FDFC" # Teal light
|
||||
textColor = "#616161" # Dark gray
|
||||
|
||||
[client]
|
||||
toolbarMode = "minimal"
|
||||
```
|
||||
|
||||
**Current implementation:**
|
||||
```bash
|
||||
cat givecalc/.streamlit/config.toml
|
||||
cat salt-amt-calculator/.streamlit/config.toml # Other calculators
|
||||
```
|
||||
|
||||
### Logo Usage
|
||||
|
||||
**Logo URLs:**
|
||||
|
||||
```python
|
||||
# Blue logo (for light backgrounds)
|
||||
LOGO_BLUE = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png"
|
||||
|
||||
# White logo (for dark backgrounds)
|
||||
LOGO_WHITE = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/white.png"
|
||||
|
||||
# SVG versions (scalable)
|
||||
LOGO_BLUE_SVG = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.svg"
|
||||
LOGO_WHITE_SVG = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/white.svg"
|
||||
```
|
||||
|
||||
**Logo placement in charts:**
|
||||
- Bottom right corner
|
||||
- 10% of chart width
|
||||
- Slightly below bottom edge (y=-0.10)
|
||||
|
||||
**Current logos:**
|
||||
```bash
|
||||
ls policyengine-app/src/images/logos/policyengine/
|
||||
```
|
||||
|
||||
### Complete Example: Branded Chart
|
||||
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
|
||||
# PolicyEngine colors
|
||||
TEAL_ACCENT = "#39C6C0"
|
||||
BLUE_PRIMARY = "#2C6496"
|
||||
|
||||
# Create chart
|
||||
fig = go.Figure()
|
||||
|
||||
# Add data
|
||||
fig.add_trace(go.Scatter(
|
||||
x=incomes,
|
||||
y=taxes,
|
||||
mode='lines',
|
||||
name='Tax liability',
|
||||
line=dict(color=TEAL_ACCENT, width=3)
|
||||
))
|
||||
|
||||
# Apply PolicyEngine branding
|
||||
fig.update_layout(
|
||||
# Typography
|
||||
font=dict(family="Roboto Serif", size=14, color="black"),
|
||||
|
||||
# Title and labels
|
||||
title="Tax liability by income",
|
||||
xaxis_title="Income",
|
||||
yaxis_title="Tax ($)",
|
||||
|
||||
# Formatting
|
||||
xaxis_tickformat="$,.0f",
|
||||
yaxis_tickformat="$,.0f",
|
||||
|
||||
# Appearance
|
||||
plot_bgcolor="white",
|
||||
template="plotly_white",
|
||||
|
||||
# Size and margins
|
||||
height=600,
|
||||
width=800,
|
||||
margin=dict(l=50, r=100, t=50, b=120, pad=4)
|
||||
)
|
||||
|
||||
# Add logo
|
||||
fig.add_layout_image(
|
||||
dict(
|
||||
source="https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png",
|
||||
xref="paper",
|
||||
yref="paper",
|
||||
x=1.0,
|
||||
y=-0.10,
|
||||
sizex=0.10,
|
||||
sizey=0.10,
|
||||
xanchor="right",
|
||||
yanchor="bottom"
|
||||
)
|
||||
)
|
||||
|
||||
# Show
|
||||
fig.show()
|
||||
```
|
||||
|
||||
## For Contributors 💻
|
||||
|
||||
### Brand Assets
|
||||
|
||||
**Repository:** PolicyEngine/policyengine-app-v2 (current), PolicyEngine/policyengine-app (legacy)
|
||||
|
||||
**Logo files:**
|
||||
```bash
|
||||
# Logos in app (both v1 and v2 use same logos)
|
||||
ls policyengine-app/src/images/logos/policyengine/
|
||||
# - blue.png - For light backgrounds
|
||||
# - white.png - For dark backgrounds
|
||||
# - blue.svg - Scalable blue logo
|
||||
# - white.svg - Scalable white logo
|
||||
# - banners/ - Banner variations
|
||||
# - profile/ - Profile/avatar versions
|
||||
```
|
||||
|
||||
**Access logos:**
|
||||
```bash
|
||||
# View logo files (v1 repo has the assets)
|
||||
cd policyengine-app/src/images/logos/policyengine/
|
||||
ls -la
|
||||
```
|
||||
|
||||
### Color Definitions
|
||||
|
||||
**⚠️ IMPORTANT: App V2 Transition**
|
||||
|
||||
PolicyEngine is transitioning to policyengine-app-v2 with updated design tokens. Use app-v2 colors for new projects.
|
||||
|
||||
**Current colors (policyengine-app-v2):**
|
||||
|
||||
```typescript
|
||||
// policyengine-app-v2/app/src/designTokens/colors.ts
|
||||
|
||||
// Primary (teal) - 50 to 900 scale
|
||||
primary[500]: "#319795" // Main teal
|
||||
primary[400]: "#38B2AC" // Lighter teal
|
||||
primary[600]: "#2C7A7B" // Darker teal
|
||||
|
||||
// Blue scale
|
||||
blue[700]: "#026AA2" // Primary blue
|
||||
blue[500]: "#0EA5E9" // Lighter blue
|
||||
|
||||
// Gray scale
|
||||
gray[700]: "#344054" // Dark text
|
||||
gray[100]: "#F2F4F7" // Light backgrounds
|
||||
|
||||
// Semantic
|
||||
success: "#22C55E"
|
||||
warning: "#FEC601"
|
||||
error: "#EF4444"
|
||||
|
||||
// Background
|
||||
background.primary: "#FFFFFF"
|
||||
background.secondary: "#F5F9FF"
|
||||
|
||||
// Text
|
||||
text.primary: "#000000"
|
||||
text.secondary: "#5A5A5A"
|
||||
```
|
||||
|
||||
**To see current design tokens:**
|
||||
```bash
|
||||
cat policyengine-app-v2/app/src/designTokens/colors.ts
|
||||
cat policyengine-app-v2/app/src/styles/colors.ts # Mantine integration
|
||||
```
|
||||
|
||||
**Legacy colors (policyengine-app - still used in some projects):**
|
||||
|
||||
```javascript
|
||||
// policyengine-app/src/style/colors.js
|
||||
TEAL_ACCENT = "#39C6C0" // Old teal (slightly different from v2)
|
||||
BLUE = "#2C6496" // Old blue
|
||||
DARK_GRAY = "#616161" // Old dark gray
|
||||
```
|
||||
|
||||
**To see legacy colors:**
|
||||
```bash
|
||||
cat policyengine-app/src/style/colors.js
|
||||
```
|
||||
|
||||
**Usage in React (app-v2):**
|
||||
```typescript
|
||||
import { colors } from 'designTokens';
|
||||
|
||||
<Button style={{ backgroundColor: colors.primary[500] }} />
|
||||
<Text style={{ color: colors.text.primary }} />
|
||||
```
|
||||
|
||||
**Usage in Python/Plotly (use legacy colors for now):**
|
||||
```python
|
||||
# For charts, continue using legacy colors until officially migrated
|
||||
TEAL_ACCENT = "#39C6C0" # From original app
|
||||
BLUE_PRIMARY = "#2C6496" # From original app
|
||||
|
||||
# Or use app-v2 colors
|
||||
TEAL_PRIMARY = "#319795" # From app-v2
|
||||
BLUE_PRIMARY_V2 = "#026AA2" # From app-v2
|
||||
```
|
||||
|
||||
### Typography
|
||||
|
||||
**Fonts:**
|
||||
|
||||
**For charts (Plotly):**
|
||||
```python
|
||||
font=dict(family="Roboto Serif")
|
||||
```
|
||||
|
||||
**For web apps:**
|
||||
```javascript
|
||||
// System font stack
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, ...
|
||||
```
|
||||
|
||||
**Loading Google Fonts:**
|
||||
```html
|
||||
<!-- In Streamlit or HTML -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Serif:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
```
|
||||
|
||||
### Chart Formatting Function
|
||||
|
||||
**Reference implementation:**
|
||||
|
||||
```bash
|
||||
# GiveCalc format_fig function
|
||||
cat givecalc/ui/visualization.py
|
||||
|
||||
# Shows:
|
||||
# - Roboto Serif font
|
||||
# - White background
|
||||
# - Logo placement
|
||||
# - Margin configuration
|
||||
```
|
||||
|
||||
**Pattern to follow:**
|
||||
```python
|
||||
def format_fig(fig: go.Figure) -> go.Figure:
|
||||
"""Format figure with PolicyEngine branding.
|
||||
|
||||
This function is used across PolicyEngine projects to ensure
|
||||
consistent chart appearance.
|
||||
"""
|
||||
# Font
|
||||
fig.update_layout(
|
||||
font=dict(family="Roboto Serif", color="black")
|
||||
)
|
||||
|
||||
# Background
|
||||
fig.update_layout(
|
||||
template="plotly_white",
|
||||
plot_bgcolor="white"
|
||||
)
|
||||
|
||||
# Size
|
||||
fig.update_layout(height=600, width=800)
|
||||
|
||||
# Margins (room for logo)
|
||||
fig.update_layout(
|
||||
margin=dict(l=50, r=100, t=50, b=120, pad=4)
|
||||
)
|
||||
|
||||
# Logo
|
||||
fig.add_layout_image(
|
||||
dict(
|
||||
source="https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png",
|
||||
xref="paper",
|
||||
yref="paper",
|
||||
x=1.0,
|
||||
y=-0.10,
|
||||
sizex=0.10,
|
||||
sizey=0.10,
|
||||
xanchor="right",
|
||||
yanchor="bottom"
|
||||
)
|
||||
)
|
||||
|
||||
# Clean modebar
|
||||
fig.update_layout(
|
||||
modebar=dict(
|
||||
bgcolor="rgba(0,0,0,0)",
|
||||
color="rgba(0,0,0,0)"
|
||||
)
|
||||
)
|
||||
|
||||
return fig
|
||||
```
|
||||
|
||||
### Streamlit Theme Configuration
|
||||
|
||||
**Standard .streamlit/config.toml:**
|
||||
|
||||
```toml
|
||||
[theme]
|
||||
base = "light"
|
||||
primaryColor = "#39C6C0" # Teal accent
|
||||
backgroundColor = "#FFFFFF" # White
|
||||
secondaryBackgroundColor = "#F7FDFC" # Teal light
|
||||
textColor = "#616161" # Dark gray
|
||||
font = "sans serif"
|
||||
|
||||
[client]
|
||||
toolbarMode = "minimal"
|
||||
showErrorDetails = true
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# Create .streamlit directory in your project
|
||||
mkdir .streamlit
|
||||
|
||||
# Copy configuration
|
||||
cat > .streamlit/config.toml << 'EOF'
|
||||
[theme]
|
||||
base = "light"
|
||||
primaryColor = "#39C6C0"
|
||||
backgroundColor = "#FFFFFF"
|
||||
secondaryBackgroundColor = "#F7FDFC"
|
||||
textColor = "#616161"
|
||||
font = "sans serif"
|
||||
EOF
|
||||
```
|
||||
|
||||
**Current examples:**
|
||||
```bash
|
||||
cat givecalc/.streamlit/config.toml
|
||||
```
|
||||
|
||||
## Design Patterns by Project Type
|
||||
|
||||
### Streamlit Calculators (GiveCalc, SALT Calculator, etc.)
|
||||
|
||||
**Required branding:**
|
||||
1. ✅ .streamlit/config.toml with PolicyEngine theme
|
||||
2. ✅ Charts use format_fig() function with logo
|
||||
3. ✅ Teal accent color for interactive elements
|
||||
4. ✅ Roboto Serif for charts
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
import streamlit as st
|
||||
import plotly.graph_objects as go
|
||||
|
||||
# Constants
|
||||
TEAL_ACCENT = "#39C6C0"
|
||||
|
||||
# Streamlit UI
|
||||
st.title("Calculator Name") # Uses theme colors automatically
|
||||
st.button("Calculate", type="primary") # Teal accent from theme
|
||||
|
||||
# Charts
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Scatter(x=x, y=y, line=dict(color=TEAL_ACCENT)))
|
||||
fig = format_fig(fig) # Add branding
|
||||
st.plotly_chart(fig)
|
||||
```
|
||||
|
||||
### Jupyter Notebooks / Analysis Scripts
|
||||
|
||||
**Required branding:**
|
||||
1. ✅ Charts use format_fig() with logo
|
||||
2. ✅ PolicyEngine color palette
|
||||
3. ✅ Roboto Serif font
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
import plotly.graph_objects as go
|
||||
|
||||
TEAL_ACCENT = "#39C6C0"
|
||||
BLUE_PRIMARY = "#2C6496"
|
||||
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Scatter(
|
||||
x=data.income,
|
||||
y=data.tax_change,
|
||||
line=dict(color=TEAL_ACCENT, width=3)
|
||||
))
|
||||
|
||||
fig.update_layout(
|
||||
font=dict(family="Roboto Serif", size=14),
|
||||
title="Tax impact by income",
|
||||
xaxis_title="Income",
|
||||
yaxis_title="Tax change ($)",
|
||||
plot_bgcolor="white"
|
||||
)
|
||||
|
||||
# Add logo
|
||||
fig.add_layout_image(...)
|
||||
```
|
||||
|
||||
### React App Components
|
||||
|
||||
**Color usage:**
|
||||
```javascript
|
||||
import colors from "style/colors";
|
||||
|
||||
// Interactive elements
|
||||
<Button style={{ backgroundColor: colors.TEAL_ACCENT }}>
|
||||
Click me
|
||||
</Button>
|
||||
|
||||
// Links
|
||||
<a style={{ color: colors.BLUE }}>Learn more</a>
|
||||
|
||||
// Text
|
||||
<p style={{ color: colors.DARK_GRAY }}>Description</p>
|
||||
```
|
||||
|
||||
**Current colors:**
|
||||
```bash
|
||||
cat policyengine-app/src/style/colors.js
|
||||
```
|
||||
|
||||
## Visual Guidelines
|
||||
|
||||
### Chart Design Principles
|
||||
|
||||
1. **Minimal decoration** - Let data speak
|
||||
2. **White backgrounds** - Clean, print-friendly
|
||||
3. **Clear axis labels** - Always include units
|
||||
4. **Formatted numbers** - Currency ($), percentages (%), etc.
|
||||
5. **Logo inclusion** - Bottom right, never intrusive
|
||||
6. **Consistent sizing** - 800x600 standard
|
||||
7. **Roboto Serif** - Professional, readable font
|
||||
|
||||
### Color Usage Rules
|
||||
|
||||
**Primary actions:**
|
||||
- Use TEAL_ACCENT (#39C6C0)
|
||||
- Buttons, highlights, current selection
|
||||
|
||||
**Chart lines:**
|
||||
- Primary data: TEAL_ACCENT or BLUE_PRIMARY
|
||||
- Secondary data: BLUE_LIGHT or GRAY
|
||||
- Negative values: DARK_RED (#b50d0d)
|
||||
|
||||
**Backgrounds:**
|
||||
- Main: WHITE (#FFFFFF)
|
||||
- Secondary: TEAL_LIGHT (#F7FDFC) or BLUE_98 (#F7FAFD)
|
||||
- Plot area: WHITE
|
||||
|
||||
**Text:**
|
||||
- Primary: BLACK (#000000)
|
||||
- Secondary: DARK_GRAY (#616161)
|
||||
- Muted: GRAY (#808080)
|
||||
|
||||
### Accessibility
|
||||
|
||||
**Color contrast requirements:**
|
||||
- Text on background: 4.5:1 minimum (WCAG AA)
|
||||
- DARK_GRAY on WHITE: ✅ Passes
|
||||
- TEAL_ACCENT on WHITE: ✅ Passes for large text
|
||||
- Use sufficient line weights for visibility
|
||||
|
||||
**Don't rely on color alone:**
|
||||
- Use patterns or labels for different data series
|
||||
- Ensure charts work in grayscale
|
||||
|
||||
## Common Branding Tasks
|
||||
|
||||
### Task 1: Create Branded Plotly Chart
|
||||
|
||||
1. **Define colors:**
|
||||
```python
|
||||
TEAL_ACCENT = "#39C6C0"
|
||||
BLUE_PRIMARY = "#2C6496"
|
||||
```
|
||||
|
||||
2. **Create chart:**
|
||||
```python
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Scatter(x=x, y=y, line=dict(color=TEAL_ACCENT)))
|
||||
```
|
||||
|
||||
3. **Apply branding:**
|
||||
```python
|
||||
fig = format_fig(fig) # See implementation above
|
||||
```
|
||||
|
||||
### Task 2: Setup Streamlit Branding
|
||||
|
||||
1. **Create config directory:**
|
||||
```bash
|
||||
mkdir .streamlit
|
||||
```
|
||||
|
||||
2. **Copy theme config:**
|
||||
```bash
|
||||
cat givecalc/.streamlit/config.toml > .streamlit/config.toml
|
||||
```
|
||||
|
||||
3. **Verify in app:**
|
||||
```python
|
||||
import streamlit as st
|
||||
|
||||
st.button("Test", type="primary") # Should be teal
|
||||
```
|
||||
|
||||
### Task 3: Brand Consistency Check
|
||||
|
||||
**Checklist:**
|
||||
- [ ] Charts use Roboto Serif font
|
||||
- [ ] Primary color is TEAL_ACCENT (#39C6C0)
|
||||
- [ ] Secondary color is BLUE_PRIMARY (#2C6496)
|
||||
- [ ] White backgrounds
|
||||
- [ ] Logo in charts (bottom right)
|
||||
- [ ] Currency formatted with $ and commas
|
||||
- [ ] Percentages formatted with %
|
||||
- [ ] Streamlit config.toml uses PolicyEngine theme
|
||||
|
||||
## Reference Implementations
|
||||
|
||||
### Excellent Examples
|
||||
|
||||
**Streamlit calculators:**
|
||||
```bash
|
||||
# GiveCalc - Complete example
|
||||
cat givecalc/ui/visualization.py
|
||||
cat givecalc/.streamlit/config.toml
|
||||
|
||||
# Other calculators
|
||||
ls salt-amt-calculator/
|
||||
ls ctc-calculator/
|
||||
```
|
||||
|
||||
**Blog post charts:**
|
||||
```bash
|
||||
# Analysis with branded charts
|
||||
cat policyengine-app/src/posts/articles/harris-eitc.md
|
||||
cat policyengine-app/src/posts/articles/montana-tax-cuts-2026.md
|
||||
```
|
||||
|
||||
**React app components:**
|
||||
```bash
|
||||
# Charts in app
|
||||
cat policyengine-app/src/pages/policy/output/DistributionalImpact.jsx
|
||||
```
|
||||
|
||||
### Don't Use These
|
||||
|
||||
**❌ Wrong colors:**
|
||||
```python
|
||||
# Don't use random colors
|
||||
color = "#FF5733"
|
||||
color = "red"
|
||||
color = "green"
|
||||
```
|
||||
|
||||
**❌ Wrong fonts:**
|
||||
```python
|
||||
# Don't use other fonts for charts
|
||||
font = dict(family="Arial")
|
||||
font = dict(family="Times New Roman")
|
||||
```
|
||||
|
||||
**❌ Missing logo:**
|
||||
```python
|
||||
# Don't skip the logo in charts for publication
|
||||
# All published charts should include PolicyEngine logo
|
||||
```
|
||||
|
||||
## Assets and Resources
|
||||
|
||||
### Logo Files
|
||||
|
||||
**In policyengine-app repository:**
|
||||
```bash
|
||||
policyengine-app/src/images/logos/policyengine/
|
||||
├── blue.png # Primary logo (light backgrounds)
|
||||
├── white.png # Logo for dark backgrounds
|
||||
├── blue.svg # Scalable blue logo
|
||||
├── white.svg # Scalable white logo
|
||||
├── banners/ # Banner variations
|
||||
└── profile/ # Profile/avatar versions
|
||||
```
|
||||
|
||||
**Raw URLs for direct use:**
|
||||
```python
|
||||
# Use these URLs in code
|
||||
LOGO_URL = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png"
|
||||
```
|
||||
|
||||
### Font Files
|
||||
|
||||
**Roboto (charts):**
|
||||
- Google Fonts: https://fonts.google.com/specimen/Roboto
|
||||
- Family: Roboto Serif
|
||||
- Weights: 300 (light), 400 (regular), 500 (medium), 700 (bold)
|
||||
|
||||
**Loading:**
|
||||
```html
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Serif:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
```
|
||||
|
||||
### Color Reference Files
|
||||
|
||||
**JavaScript (React app):**
|
||||
```bash
|
||||
cat policyengine-app/src/style/colors.js
|
||||
```
|
||||
|
||||
**Python (calculators, analysis):**
|
||||
```python
|
||||
# Define in constants.py or at top of file
|
||||
TEAL_ACCENT = "#39C6C0"
|
||||
BLUE_PRIMARY = "#2C6496"
|
||||
DARK_GRAY = "#616161"
|
||||
WHITE = "#FFFFFF"
|
||||
```
|
||||
|
||||
## Brand Evolution
|
||||
|
||||
**Current identity (2025):**
|
||||
- Teal primary (#39C6C0)
|
||||
- Blue secondary (#2C6496)
|
||||
- Roboto Serif for charts
|
||||
- Minimal, data-focused design
|
||||
|
||||
**If brand evolves:**
|
||||
- Colors defined in policyengine-app/src/style/colors.js are source of truth
|
||||
- Update this skill to point to current definitions
|
||||
- Never hardcode - always reference colors.js
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Color Codes
|
||||
|
||||
| Color | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| Teal Accent | #39C6C0 | Primary interactive elements |
|
||||
| Blue Primary | #2C6496 | Secondary, links, charts |
|
||||
| Dark Gray | #616161 | Body text |
|
||||
| White | #FFFFFF | Backgrounds |
|
||||
| Teal Light | #F7FDFC | Secondary backgrounds |
|
||||
| Dark Red | #b50d0d | Negative values, errors |
|
||||
|
||||
### Font Families
|
||||
|
||||
| Context | Font |
|
||||
|---------|------|
|
||||
| Charts | Roboto Serif |
|
||||
| Web app | System sans-serif |
|
||||
| Streamlit | Default sans-serif |
|
||||
| Code blocks | Monospace |
|
||||
|
||||
### Logo URLs
|
||||
|
||||
| Background | Format | URL |
|
||||
|------------|--------|-----|
|
||||
| Light | PNG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png |
|
||||
| Light | SVG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.svg |
|
||||
| Dark | PNG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/white.png |
|
||||
| Dark | SVG | https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/white.svg |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- **policyengine-app-skill** - React component styling
|
||||
- **policyengine-analysis-skill** - Chart creation patterns
|
||||
- **policyengine-writing-skill** - Content style (complements visual style)
|
||||
|
||||
## Resources
|
||||
|
||||
**Brand assets:** PolicyEngine/policyengine-app/src/images/
|
||||
**Color definitions:** PolicyEngine/policyengine-app/src/style/colors.js
|
||||
**Examples:** givecalc, salt-amt-calculator, crfb-tob-impacts
|
||||
768
skills/documentation/policyengine-standards-skill/SKILL.md
Normal file
768
skills/documentation/policyengine-standards-skill/SKILL.md
Normal file
@@ -0,0 +1,768 @@
|
||||
---
|
||||
name: policyengine-standards
|
||||
description: PolicyEngine coding standards, formatters, CI requirements, and development best practices
|
||||
---
|
||||
|
||||
# PolicyEngine Standards Skill
|
||||
|
||||
Use this skill to ensure code meets PolicyEngine's development standards and passes CI checks.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Before committing code to any PolicyEngine repository
|
||||
- When CI checks fail with linting/formatting errors
|
||||
- Setting up a new PolicyEngine repository
|
||||
- Reviewing PRs for standard compliance
|
||||
- When AI tools generate code that needs standardization
|
||||
|
||||
## Critical Requirements
|
||||
|
||||
### Python Version
|
||||
⚠️ **MUST USE Python 3.13** - Do NOT downgrade to older versions
|
||||
- Check version: `python --version`
|
||||
- Use `pyproject.toml` to specify version requirements
|
||||
|
||||
### Command Execution
|
||||
⚠️ **ALWAYS use `uv run` for Python commands** - Never use bare `python` or `pytest`
|
||||
- ✅ Correct: `uv run python script.py`, `uv run pytest tests/`
|
||||
- ❌ Wrong: `python script.py`, `pytest tests/`
|
||||
- This ensures correct virtual environment and dependencies
|
||||
|
||||
### Documentation (Python Projects)
|
||||
⚠️ **MUST USE Jupyter Book 2.0 (MyST-NB)** - NOT Jupyter Book 1.x
|
||||
- Build docs: `myst build docs` (NOT `jb build`)
|
||||
- Use MyST markdown syntax
|
||||
|
||||
## Before Committing - Checklist
|
||||
|
||||
1. **Write tests first** (TDD - see below)
|
||||
2. **Format code**: `make format` or language-specific formatter
|
||||
3. **Run tests**: `make test` to ensure all tests pass
|
||||
4. **Check linting**: Ensure no linting errors
|
||||
5. **Use config files**: Prefer config files over environment variables
|
||||
6. **Reference issues**: Include "Fixes #123" in commit message
|
||||
|
||||
## Creating Pull Requests
|
||||
|
||||
### The CI Waiting Problem
|
||||
|
||||
**Common failure pattern:**
|
||||
```
|
||||
User: "Create a PR and mark it ready when CI passes"
|
||||
Claude: "I've created the PR as draft. CI will take a while, I'll check back later..."
|
||||
[Chat ends - Claude never checks back]
|
||||
Result: PR stays in draft, user has to manually check CI and mark ready
|
||||
```
|
||||
|
||||
### Solution: Use /create-pr Command
|
||||
|
||||
**When creating PRs, use the /create-pr command:**
|
||||
|
||||
```bash
|
||||
/create-pr
|
||||
```
|
||||
|
||||
**This command:**
|
||||
- ✅ Creates PR as draft
|
||||
- ✅ Actually waits for CI (polls every 15 seconds)
|
||||
- ✅ Marks ready when CI passes
|
||||
- ✅ Reports failures with details
|
||||
- ✅ Handles timeouts gracefully
|
||||
|
||||
**Why this works:**
|
||||
The command contains explicit polling logic that Claude executes, so it actually waits instead of giving up.
|
||||
|
||||
### If /create-pr is Not Available
|
||||
|
||||
**If the command isn't installed, implement the pattern directly:**
|
||||
|
||||
```bash
|
||||
# 1. Create PR as draft
|
||||
gh pr create --draft --title "Title" --body "Body"
|
||||
PR_NUMBER=$(gh pr view --json number --jq '.number')
|
||||
|
||||
# 2. Wait for CI (ACTUALLY WAIT - don't give up!)
|
||||
POLL_INTERVAL=15
|
||||
ELAPSED=0
|
||||
|
||||
while true; do # No timeout - wait as long as needed
|
||||
CHECKS=$(gh pr checks $PR_NUMBER --json status,conclusion)
|
||||
TOTAL=$(echo "$CHECKS" | jq '. | length')
|
||||
COMPLETED=$(echo "$CHECKS" | jq '[.[] | select(.status == "COMPLETED")] | length')
|
||||
|
||||
echo "[$ELAPSED s] CI: $COMPLETED/$TOTAL completed"
|
||||
|
||||
if [ "$COMPLETED" -eq "$TOTAL" ] && [ "$TOTAL" -gt 0 ]; then
|
||||
FAILED=$(echo "$CHECKS" | jq '[.[] | select(.conclusion == "FAILURE")] | length')
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "✅ All CI passed! Marking ready..."
|
||||
gh pr ready $PR_NUMBER
|
||||
break
|
||||
else
|
||||
echo "❌ CI failed. PR remains draft."
|
||||
gh pr checks $PR_NUMBER
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep $POLL_INTERVAL
|
||||
ELAPSED=$((ELAPSED + POLL_INTERVAL))
|
||||
done
|
||||
|
||||
# Important: No timeout! Population simulations can take 30+ minutes.
|
||||
```
|
||||
|
||||
### DO NOT Say "I'll Check Back Later"
|
||||
|
||||
**❌ WRONG:**
|
||||
```
|
||||
"I've created the PR as draft. CI checks will take a few minutes.
|
||||
I'll check back later once they complete."
|
||||
```
|
||||
|
||||
**Why wrong:** You cannot check back later. The chat session ends.
|
||||
|
||||
**✅ CORRECT:**
|
||||
```
|
||||
"I've created the PR as draft. Now polling CI status every 15 seconds..."
|
||||
[Actually polls using while loop]
|
||||
"CI checks completed. All passed! Marking PR as ready for review."
|
||||
```
|
||||
|
||||
### When to Create Draft vs Ready
|
||||
|
||||
**Always create as draft when:**
|
||||
- CI checks are configured
|
||||
- User asks to wait for CI
|
||||
- Making automated changes
|
||||
- Unsure if CI will pass
|
||||
|
||||
**Create as ready only when:**
|
||||
- User explicitly requests ready PR
|
||||
- No CI configured
|
||||
- CI already verified locally
|
||||
|
||||
### PR Workflow Standards
|
||||
|
||||
**Standard flow:**
|
||||
```bash
|
||||
# 1. Ensure branch is pushed
|
||||
git push -u origin feature-branch
|
||||
|
||||
# 2. Create PR as draft
|
||||
gh pr create --draft --title "..." --body "..."
|
||||
|
||||
# 3. Wait for CI (use polling loop - see pattern above)
|
||||
|
||||
# 4. If CI passes:
|
||||
gh pr ready $PR_NUMBER
|
||||
|
||||
# 5. If CI fails:
|
||||
echo "CI failed. PR remains draft. Fix issues and push again."
|
||||
```
|
||||
|
||||
## Test-Driven Development (TDD)
|
||||
|
||||
PolicyEngine follows Test-Driven Development practices across all repositories.
|
||||
|
||||
### TDD Workflow
|
||||
|
||||
**1. Write test first (RED):**
|
||||
```python
|
||||
# tests/test_new_feature.py
|
||||
def test_california_eitc_calculation():
|
||||
"""Test California EITC for family with 2 children earning $30,000."""
|
||||
situation = create_family(income=30000, num_children=2, state="CA")
|
||||
sim = Simulation(situation=situation)
|
||||
ca_eitc = sim.calculate("ca_eitc", 2024)[0]
|
||||
|
||||
# Test fails initially (feature not implemented yet)
|
||||
assert ca_eitc == 3000, "CA EITC should be $3,000 for this household"
|
||||
```
|
||||
|
||||
**2. Implement feature (GREEN):**
|
||||
```python
|
||||
# policyengine_us/variables/gov/states/ca/tax/income/credits/ca_eitc.py
|
||||
class ca_eitc(Variable):
|
||||
value_type = float
|
||||
entity = TaxUnit
|
||||
definition_period = YEAR
|
||||
|
||||
def formula(tax_unit, period, parameters):
|
||||
# Implementation to make test pass
|
||||
federal_eitc = tax_unit("eitc", period)
|
||||
return federal_eitc * parameters(period).gov.states.ca.tax.eitc.match
|
||||
```
|
||||
|
||||
**3. Refactor (REFACTOR):**
|
||||
```python
|
||||
# Clean up, optimize, add documentation
|
||||
# All while tests continue to pass
|
||||
```
|
||||
|
||||
### TDD Benefits
|
||||
|
||||
**Why PolicyEngine uses TDD:**
|
||||
- ✅ **Accuracy** - Tests verify implementation matches regulations
|
||||
- ✅ **Documentation** - Tests show expected behavior
|
||||
- ✅ **Regression prevention** - Changes don't break existing features
|
||||
- ✅ **Confidence** - Safe to refactor
|
||||
- ✅ **Isolation** - Multi-agent workflow (test-creator and rules-engineer work separately)
|
||||
|
||||
### TDD in Multi-Agent Workflow
|
||||
|
||||
**Country model development:**
|
||||
1. **@document-collector** gathers regulations
|
||||
2. **@test-creator** writes tests from regulations (isolated, no implementation access)
|
||||
3. **@rules-engineer** implements from regulations (isolated, no test access)
|
||||
4. Both work from same source → tests verify implementation accuracy
|
||||
|
||||
**See policyengine-core-skill and country-models agents for details.**
|
||||
|
||||
### Test Examples
|
||||
|
||||
**Python (pytest):**
|
||||
```python
|
||||
def test_ctc_for_two_children():
|
||||
"""Test CTC calculation for married couple with 2 children."""
|
||||
situation = create_married_couple(
|
||||
income_1=75000,
|
||||
income_2=50000,
|
||||
num_children=2,
|
||||
child_ages=[5, 8]
|
||||
)
|
||||
|
||||
sim = Simulation(situation=situation)
|
||||
ctc = sim.calculate("ctc", 2024)[0]
|
||||
|
||||
assert ctc == 4000, "CTC should be $2,000 per child"
|
||||
```
|
||||
|
||||
**React (Jest + RTL):**
|
||||
```javascript
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import TaxCalculator from './TaxCalculator';
|
||||
|
||||
test('displays calculated tax', () => {
|
||||
render(<TaxCalculator income={50000} />);
|
||||
|
||||
// Test what user sees, not implementation
|
||||
expect(screen.getByText(/\$5,000/)).toBeInTheDocument();
|
||||
});
|
||||
```
|
||||
|
||||
### Test Organization
|
||||
|
||||
**Python:**
|
||||
```
|
||||
tests/
|
||||
├── test_variables/
|
||||
│ ├── test_income.py
|
||||
│ ├── test_deductions.py
|
||||
│ └── test_credits.py
|
||||
├── test_parameters/
|
||||
└── test_simulations/
|
||||
```
|
||||
|
||||
**React:**
|
||||
```
|
||||
src/
|
||||
├── components/
|
||||
│ └── TaxCalculator/
|
||||
│ ├── TaxCalculator.jsx
|
||||
│ └── TaxCalculator.test.jsx
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
**Python:**
|
||||
```bash
|
||||
# All tests
|
||||
make test
|
||||
|
||||
# With uv
|
||||
uv run pytest tests/ -v
|
||||
|
||||
# Specific test
|
||||
uv run pytest tests/test_credits.py::test_ctc_for_two_children -v
|
||||
|
||||
# With coverage
|
||||
uv run pytest tests/ --cov=policyengine_us --cov-report=html
|
||||
```
|
||||
|
||||
**React:**
|
||||
```bash
|
||||
# All tests
|
||||
make test
|
||||
|
||||
# Watch mode
|
||||
npm test -- --watch
|
||||
|
||||
# Specific test
|
||||
npm test -- TaxCalculator.test.jsx
|
||||
|
||||
# Coverage
|
||||
npm test -- --coverage
|
||||
```
|
||||
|
||||
### Test Quality Standards
|
||||
|
||||
**Good tests:**
|
||||
- ✅ Test behavior, not implementation
|
||||
- ✅ Clear, descriptive names
|
||||
- ✅ Single assertion per test (when possible)
|
||||
- ✅ Include documentation (docstrings)
|
||||
- ✅ Based on official regulations with citations
|
||||
|
||||
**Bad tests:**
|
||||
- ❌ Testing private methods
|
||||
- ❌ Mocking everything
|
||||
- ❌ No assertion messages
|
||||
- ❌ Magic numbers without explanation
|
||||
|
||||
### Example: TDD for New Feature
|
||||
|
||||
```python
|
||||
# Step 1: Write test (RED)
|
||||
def test_new_york_empire_state_child_credit():
|
||||
"""Test NY Empire State Child Credit for family with 1 child.
|
||||
|
||||
Based on NY Tax Law Section 606(c-1).
|
||||
Family earning $50,000 with 1 child under 4 should receive $330.
|
||||
"""
|
||||
situation = create_family(
|
||||
income=50000,
|
||||
num_children=1,
|
||||
child_ages=[2],
|
||||
state="NY"
|
||||
)
|
||||
|
||||
sim = Simulation(situation=situation)
|
||||
credit = sim.calculate("ny_empire_state_child_credit", 2024)[0]
|
||||
|
||||
assert credit == 330, "Should receive $330 for child under 4"
|
||||
|
||||
# Test fails - feature doesn't exist yet
|
||||
|
||||
# Step 2: Implement (GREEN)
|
||||
# Create variable in policyengine_us/variables/gov/states/ny/...
|
||||
# Test passes
|
||||
|
||||
# Step 3: Refactor
|
||||
# Optimize, add documentation, maintain passing tests
|
||||
```
|
||||
|
||||
## Python Standards
|
||||
|
||||
### Formatting
|
||||
- **Formatter**: Black with 79-character line length
|
||||
- **Command**: `make format` or `black . -l 79`
|
||||
- **Check without changes**: `black . -l 79 --check`
|
||||
|
||||
```bash
|
||||
# Format all Python files
|
||||
make format
|
||||
|
||||
# Check if formatting is needed (CI-style)
|
||||
black . -l 79 --check
|
||||
```
|
||||
|
||||
### Code Style
|
||||
```python
|
||||
# Imports: Grouped and alphabetized
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path # stdlib
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd # third-party
|
||||
|
||||
from policyengine_us import Simulation # local
|
||||
|
||||
# Naming conventions
|
||||
class TaxCalculator: # CamelCase for classes
|
||||
pass
|
||||
|
||||
def calculate_income_tax(income): # snake_case for functions
|
||||
annual_income = income * 12 # snake_case for variables
|
||||
return annual_income
|
||||
|
||||
# Type hints (recommended)
|
||||
def calculate_tax(income: float, state: str) -> float:
|
||||
"""Calculate state income tax.
|
||||
|
||||
Args:
|
||||
income: Annual income in dollars
|
||||
state: Two-letter state code
|
||||
|
||||
Returns:
|
||||
Tax liability in dollars
|
||||
"""
|
||||
pass
|
||||
|
||||
# Error handling - catch specific exceptions
|
||||
try:
|
||||
result = simulation.calculate("income_tax", 2024)
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Invalid variable name: {e}")
|
||||
```
|
||||
|
||||
### Testing
|
||||
```python
|
||||
import pytest
|
||||
|
||||
def test_ctc_calculation():
|
||||
"""Test Child Tax Credit calculation for family with 2 children."""
|
||||
situation = create_family(income=50000, num_children=2)
|
||||
sim = Simulation(situation=situation)
|
||||
ctc = sim.calculate("ctc", 2024)[0]
|
||||
|
||||
assert ctc == 4000, "CTC should be $2000 per child"
|
||||
```
|
||||
|
||||
**Run tests:**
|
||||
```bash
|
||||
# All tests
|
||||
make test
|
||||
|
||||
# Or with uv
|
||||
uv run pytest tests/ -v
|
||||
|
||||
# Specific test
|
||||
uv run pytest tests/test_tax.py::test_ctc_calculation -v
|
||||
|
||||
# With coverage
|
||||
uv run pytest tests/ --cov=policyengine_us --cov-report=html
|
||||
```
|
||||
|
||||
## JavaScript/React Standards
|
||||
|
||||
### Formatting
|
||||
- **Formatters**: Prettier + ESLint
|
||||
- **Command**: `npm run lint -- --fix && npx prettier --write .`
|
||||
- **CI Check**: `npm run lint -- --max-warnings=0`
|
||||
|
||||
```bash
|
||||
# Format all files
|
||||
make format
|
||||
|
||||
# Or manually
|
||||
npm run lint -- --fix
|
||||
npx prettier --write .
|
||||
|
||||
# Check if formatting is needed (CI-style)
|
||||
npm run lint -- --max-warnings=0
|
||||
```
|
||||
|
||||
### Code Style
|
||||
```javascript
|
||||
// Use functional components only (no class components)
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
function TaxCalculator({ income, state }) {
|
||||
const [tax, setTax] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
// Calculate tax when inputs change
|
||||
calculateTax(income, state).then(setTax);
|
||||
}, [income, state]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Tax: ${tax.toLocaleString()}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// File naming
|
||||
// - Components: PascalCase.jsx (TaxCalculator.jsx)
|
||||
// - Utilities: camelCase.js (formatCurrency.js)
|
||||
|
||||
// Environment config - use config file pattern
|
||||
// src/config/environment.js
|
||||
const config = {
|
||||
API_URL: process.env.NODE_ENV === 'production'
|
||||
? 'https://api.policyengine.org'
|
||||
: 'http://localhost:5000'
|
||||
};
|
||||
export default config;
|
||||
```
|
||||
|
||||
### React Component Size
|
||||
- Keep components under 150 lines after formatting
|
||||
- Extract complex logic into custom hooks
|
||||
- Split large components into smaller ones
|
||||
|
||||
## Version Control Standards
|
||||
|
||||
### Changelog Management
|
||||
|
||||
**CRITICAL**: For PRs, ONLY modify `changelog_entry.yaml`. NEVER manually update `CHANGELOG.md` or `changelog.yaml`.
|
||||
|
||||
**Correct Workflow:**
|
||||
1. Create `changelog_entry.yaml` at repository root:
|
||||
```yaml
|
||||
- bump: patch # or minor, major
|
||||
changes:
|
||||
added:
|
||||
- Description of new feature
|
||||
fixed:
|
||||
- Description of bug fix
|
||||
changed:
|
||||
- Description of change
|
||||
```
|
||||
|
||||
2. Commit ONLY `changelog_entry.yaml` with your code changes
|
||||
|
||||
3. GitHub Actions automatically updates `CHANGELOG.md` and `changelog.yaml` on merge
|
||||
|
||||
**DO NOT:**
|
||||
- ❌ Run `make changelog` manually during PR creation
|
||||
- ❌ Commit `CHANGELOG.md` or `changelog.yaml` in your PR
|
||||
- ❌ Modify main changelog files directly
|
||||
|
||||
### Git Workflow
|
||||
|
||||
1. **Create branches on PolicyEngine repos, NOT forks**
|
||||
- Forks cause CI failures due to missing secrets
|
||||
- Request write access if needed
|
||||
|
||||
2. **Branch naming**: `feature-name` or `fix-issue-123`
|
||||
|
||||
3. **Commit messages**:
|
||||
```
|
||||
Add CTC reform analysis for CRFB report
|
||||
|
||||
- Implement household-level calculations
|
||||
- Add state-by-state comparison
|
||||
- Create visualizations
|
||||
|
||||
Fixes #123
|
||||
```
|
||||
|
||||
4. **PR description**: Include "Fixes #123" to auto-close issues
|
||||
|
||||
### Common Git Pitfalls
|
||||
|
||||
**Never do these:**
|
||||
- ❌ Force push to main/master
|
||||
- ❌ Commit secrets or `.env` files
|
||||
- ❌ Skip hooks with `--no-verify`
|
||||
- ❌ Create versioned files (app_v2.py, component_new.jsx)
|
||||
|
||||
**Always do:**
|
||||
- ✅ Fix original files in place
|
||||
- ✅ Run formatters before pushing
|
||||
- ✅ Reference issue numbers in commits
|
||||
- ✅ Watch CI after filing PR
|
||||
|
||||
## Common AI Pitfalls
|
||||
|
||||
Since many PRs are AI-generated, watch for these common mistakes:
|
||||
|
||||
### 1. File Versioning
|
||||
**❌ Wrong:**
|
||||
```bash
|
||||
# Creating new versions instead of fixing originals
|
||||
app_new.py
|
||||
app_v2.py
|
||||
component_refactored.jsx
|
||||
```
|
||||
|
||||
**✅ Correct:**
|
||||
```bash
|
||||
# Always modify the original file
|
||||
app.py # Fixed in place
|
||||
```
|
||||
|
||||
### 2. Formatter Not Run
|
||||
**❌ Wrong:** Committing without formatting (main cause of CI failures)
|
||||
|
||||
**✅ Correct:**
|
||||
```bash
|
||||
# Python
|
||||
make format
|
||||
black . -l 79
|
||||
|
||||
# React
|
||||
npm run lint -- --fix
|
||||
npx prettier --write .
|
||||
```
|
||||
|
||||
### 3. Environment Variables
|
||||
**❌ Wrong:**
|
||||
```javascript
|
||||
// React env vars without REACT_APP_ prefix
|
||||
const API_URL = process.env.API_URL; // Won't work!
|
||||
```
|
||||
|
||||
**✅ Correct:**
|
||||
```javascript
|
||||
// Use config file pattern instead
|
||||
import config from './config/environment';
|
||||
const API_URL = config.API_URL;
|
||||
```
|
||||
|
||||
### 4. Using Wrong Python Version
|
||||
**❌ Wrong:** Downgrading to Python 3.10 or older
|
||||
|
||||
**✅ Correct:** Use Python 3.13 as specified in project requirements
|
||||
|
||||
### 5. Manual Changelog Updates
|
||||
**❌ Wrong:** Running `make changelog` and committing `CHANGELOG.md`
|
||||
|
||||
**✅ Correct:** Only create `changelog_entry.yaml` in PR
|
||||
|
||||
## Repository Setup Patterns
|
||||
|
||||
### Python Package Structure
|
||||
```
|
||||
policyengine-package/
|
||||
├── policyengine_package/
|
||||
│ ├── __init__.py
|
||||
│ ├── core/
|
||||
│ ├── calculations/
|
||||
│ └── utils/
|
||||
├── tests/
|
||||
│ ├── test_calculations.py
|
||||
│ └── test_core.py
|
||||
├── pyproject.toml
|
||||
├── Makefile
|
||||
├── CLAUDE.md
|
||||
├── CHANGELOG.md
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### React App Structure
|
||||
```
|
||||
policyengine-app/
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ ├── pages/
|
||||
│ ├── config/
|
||||
│ │ └── environment.js
|
||||
│ └── App.jsx
|
||||
├── public/
|
||||
├── package.json
|
||||
├── .eslintrc.json
|
||||
├── .prettierrc
|
||||
└── README.md
|
||||
```
|
||||
|
||||
## Makefile Commands
|
||||
|
||||
Standard commands across PolicyEngine repos:
|
||||
|
||||
```bash
|
||||
make install # Install dependencies
|
||||
make test # Run tests
|
||||
make format # Format code
|
||||
make changelog # Update changelog (automation only, not manual)
|
||||
make debug # Start dev server (apps)
|
||||
make build # Production build (apps)
|
||||
```
|
||||
|
||||
## CI Stability
|
||||
|
||||
### Common CI Issues
|
||||
|
||||
**1. Fork PRs Fail**
|
||||
- **Problem**: PRs from forks don't have access to repository secrets
|
||||
- **Solution**: Create branches directly on PolicyEngine repos
|
||||
|
||||
**2. GitHub API Rate Limits**
|
||||
- **Problem**: Smoke tests fail with 403 errors
|
||||
- **Solution**: Re-run failed jobs (different runners have different limits)
|
||||
|
||||
**3. Linting Failures**
|
||||
- **Problem**: Code not formatted before commit
|
||||
- **Solution**: Always run `make format` before committing
|
||||
|
||||
**4. Test Failures in CI but Pass Locally**
|
||||
- **Problem**: Missing `uv run` prefix
|
||||
- **Solution**: Use `uv run pytest` instead of `pytest`
|
||||
|
||||
## Best Practices Checklist
|
||||
|
||||
### Code Quality
|
||||
- [ ] Code formatted with Black (Python) or Prettier (JS)
|
||||
- [ ] No linting errors
|
||||
- [ ] All tests pass
|
||||
- [ ] Type hints added (Python, where applicable)
|
||||
- [ ] Docstrings for public functions/classes
|
||||
- [ ] Error handling with specific exceptions
|
||||
|
||||
### Version Control
|
||||
- [ ] Only `changelog_entry.yaml` created (not CHANGELOG.md)
|
||||
- [ ] Commit message references issue number
|
||||
- [ ] Branch created on PolicyEngine repo (not fork)
|
||||
- [ ] No secrets or .env files committed
|
||||
- [ ] Original files modified (no _v2 or _new files)
|
||||
|
||||
### Testing
|
||||
- [ ] Tests written for new functionality
|
||||
- [ ] Tests pass locally with `make test`
|
||||
- [ ] Coverage maintained or improved
|
||||
- [ ] Edge cases handled
|
||||
|
||||
### Documentation
|
||||
- [ ] README updated if needed
|
||||
- [ ] Code comments for complex logic
|
||||
- [ ] API documentation updated if needed
|
||||
- [ ] Examples provided for new features
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Format Commands by Language
|
||||
|
||||
**Python:**
|
||||
```bash
|
||||
make format # Format code
|
||||
black . -l 79 --check # Check formatting
|
||||
uv run pytest tests/ -v # Run tests
|
||||
```
|
||||
|
||||
**React:**
|
||||
```bash
|
||||
make format # Format code
|
||||
npm run lint -- --max-warnings=0 # Check linting
|
||||
npm test # Run tests
|
||||
```
|
||||
|
||||
### Pre-Commit Checklist
|
||||
```bash
|
||||
# 1. Format
|
||||
make format
|
||||
|
||||
# 2. Test
|
||||
make test
|
||||
|
||||
# 3. Check linting
|
||||
# Python: black . -l 79 --check
|
||||
# React: npm run lint -- --max-warnings=0
|
||||
|
||||
# 4. Stage and commit
|
||||
git add .
|
||||
git commit -m "Description
|
||||
|
||||
Fixes #123"
|
||||
|
||||
# 5. Push and watch CI
|
||||
git push
|
||||
```
|
||||
|
||||
## Resources
|
||||
|
||||
- **Main CLAUDE.md**: `/PolicyEngine/CLAUDE.md`
|
||||
- **Python Style**: PEP 8, Black documentation
|
||||
- **React Style**: Airbnb React/JSX Style Guide
|
||||
- **Testing**: pytest documentation, Jest/RTL documentation
|
||||
- **Writing Style**: See policyengine-writing-skill for blog posts, PR descriptions, and documentation
|
||||
|
||||
## Examples
|
||||
|
||||
See PolicyEngine repositories for examples of standard-compliant code:
|
||||
- **policyengine-us**: Python package standards
|
||||
- **policyengine-app**: React app standards
|
||||
- **givecalc**: Streamlit app standards
|
||||
- **crfb-tob-impacts**: Analysis repository standards
|
||||
295
skills/documentation/policyengine-user-guide-skill/SKILL.md
Normal file
295
skills/documentation/policyengine-user-guide-skill/SKILL.md
Normal file
@@ -0,0 +1,295 @@
|
||||
---
|
||||
name: policyengine-user-guide
|
||||
description: Using PolicyEngine web apps to analyze tax and benefit policy impacts - for users of policyengine.org
|
||||
---
|
||||
|
||||
# PolicyEngine User Guide
|
||||
|
||||
This skill helps you use PolicyEngine to analyze how tax and benefit policies affect households and populations.
|
||||
|
||||
## For Users: Getting Started
|
||||
|
||||
### What is PolicyEngine?
|
||||
|
||||
PolicyEngine computes the impact of public policy on households and society. You can:
|
||||
- Calculate how policies affect your household
|
||||
- Analyze population-wide impacts of reforms
|
||||
- Create and share custom policy proposals
|
||||
- Compare different policy options
|
||||
|
||||
### Web App: policyengine.org
|
||||
|
||||
**Main features:**
|
||||
1. **Your household** - Calculate your taxes and benefits
|
||||
2. **Policy** - Design custom reforms and see impacts
|
||||
3. **Research** - Read policy analysis and blog posts
|
||||
|
||||
### Available Countries
|
||||
|
||||
- **United States** - policyengine.org/us
|
||||
- **United Kingdom** - policyengine.org/uk
|
||||
- **Canada** - policyengine.org/ca (beta)
|
||||
|
||||
## Using the Household Calculator
|
||||
|
||||
### Step 1: Navigate to Household Page
|
||||
|
||||
**US:** https://policyengine.org/us/household
|
||||
**UK:** https://policyengine.org/uk/household
|
||||
|
||||
### Step 2: Enter Your Information
|
||||
|
||||
**Income:**
|
||||
- Employment income (W-2 wages)
|
||||
- Self-employment income
|
||||
- Capital gains and dividends
|
||||
- Social Security, pensions, etc.
|
||||
|
||||
**Household composition:**
|
||||
- Adults and dependents
|
||||
- Ages
|
||||
- Marital status
|
||||
|
||||
**Location:**
|
||||
- State (US) or region (UK)
|
||||
- NYC checkbox for New York City residents
|
||||
|
||||
**Deductions (US):**
|
||||
- Charitable donations
|
||||
- Mortgage interest
|
||||
- State and local taxes (SALT)
|
||||
- Medical expenses
|
||||
|
||||
### Step 3: View Results
|
||||
|
||||
**Net income** - Your income after taxes and benefits
|
||||
|
||||
**Breakdown:**
|
||||
- Total taxes (federal + state + local)
|
||||
- Total benefits (EITC, CTC, SNAP, etc.)
|
||||
- Effective tax rate
|
||||
- Marginal tax rate
|
||||
|
||||
**Charts:**
|
||||
- Net income by earnings
|
||||
- Marginal tax rate by earnings
|
||||
|
||||
## Creating a Policy Reform
|
||||
|
||||
### Step 1: Navigate to Policy Page
|
||||
|
||||
**US:** https://policyengine.org/us/policy
|
||||
**UK:** https://policyengine.org/uk/policy
|
||||
|
||||
### Step 2: Select Parameters to Change
|
||||
|
||||
**Browse parameters by:**
|
||||
- Government department (IRS, SSA, etc.)
|
||||
- Program (EITC, CTC, SNAP)
|
||||
- Type (tax rates, benefit amounts, thresholds)
|
||||
|
||||
**Example: Increase Child Tax Credit**
|
||||
1. Navigate to gov.irs.credits.ctc.amount.base_amount
|
||||
2. Change from $2,000 to $5,000
|
||||
3. Click "Calculate economic impact"
|
||||
|
||||
### Step 3: View Population Impacts
|
||||
|
||||
**Budgetary impact:**
|
||||
- Total cost or revenue raised
|
||||
- Breakdown by program
|
||||
|
||||
**Poverty impact:**
|
||||
- Change in poverty rates
|
||||
- By age group (children, adults, seniors)
|
||||
- Deep poverty (income < 50% of threshold)
|
||||
|
||||
**Distributional impact:**
|
||||
- Average impact by income decile
|
||||
- Winners and losers by decile
|
||||
- Relative vs absolute changes
|
||||
|
||||
**Inequality impact:**
|
||||
- Gini index change
|
||||
- Top 10% and top 1% income share
|
||||
|
||||
### Step 4: Share Your Reform
|
||||
|
||||
**Share URL:**
|
||||
Every reform has a unique URL you can share:
|
||||
```
|
||||
policyengine.org/us/policy?reform=12345®ion=enhanced_us&timePeriod=2025
|
||||
```
|
||||
|
||||
**Parameters in URL:**
|
||||
- `reform=12345` - Your custom reform ID
|
||||
- `region=enhanced_us` - Geography (US, state, or congressional district)
|
||||
- `timePeriod=2025` - Year of analysis
|
||||
|
||||
## Understanding Results
|
||||
|
||||
### Metrics Explained
|
||||
|
||||
**Supplemental Poverty Measure (SPM):**
|
||||
- Accounts for taxes, benefits, and living costs
|
||||
- US Census Bureau's official alternative poverty measure
|
||||
- More comprehensive than Official Poverty Measure
|
||||
|
||||
**Gini coefficient:**
|
||||
- Measures income inequality (0 = perfect equality, 1 = perfect inequality)
|
||||
- US Gini is typically around 0.48
|
||||
- Lower values = more equal income distribution
|
||||
|
||||
**Income deciles:**
|
||||
- Population divided into 10 equal groups by income
|
||||
- Decile 1 = bottom 10% of earners
|
||||
- Decile 10 = top 10% of earners
|
||||
|
||||
**Winners and losers:**
|
||||
- Winners: Net income increases by 5% or more
|
||||
- Losers: Net income decreases by 5% or more
|
||||
- Neutral: Net income change less than 5%
|
||||
|
||||
### Reading Charts
|
||||
|
||||
**Household impact charts:**
|
||||
- X-axis: Usually income or earnings
|
||||
- Y-axis: Net income, taxes, or benefits
|
||||
- Hover to see exact values
|
||||
|
||||
**Population impact charts:**
|
||||
- Bar charts: Compare across groups (deciles, states)
|
||||
- Line charts: Show relationships (income vs impact)
|
||||
- Waterfall charts: Show components of budgetary impact
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Use Case 1: How Does Policy X Affect My Household?
|
||||
|
||||
1. Go to household calculator
|
||||
2. Enter your information
|
||||
3. Select "Reform" and choose the policy
|
||||
4. Compare baseline vs reform results
|
||||
|
||||
### Use Case 2: How Much Would Policy X Cost?
|
||||
|
||||
1. Go to policy page
|
||||
2. Create or select the reform
|
||||
3. View "Budgetary impact" section
|
||||
4. See total cost and breakdown
|
||||
|
||||
### Use Case 3: Would Policy X Reduce Poverty?
|
||||
|
||||
1. Go to policy page
|
||||
2. Create or select the reform
|
||||
3. View "Poverty impact" section
|
||||
4. See change in poverty rate by age group
|
||||
|
||||
### Use Case 4: Who Benefits from Policy X?
|
||||
|
||||
1. Go to policy page
|
||||
2. Create or select the reform
|
||||
3. View "Distributional impact" section
|
||||
4. See winners and losers by income decile
|
||||
|
||||
### Use Case 5: Compare Two Policy Proposals
|
||||
|
||||
1. Create Reform A (e.g., expand EITC)
|
||||
2. Note the URL or reform ID
|
||||
3. Create Reform B (e.g., expand CTC)
|
||||
4. Compare budgetary, poverty, and distributional impacts
|
||||
|
||||
## For Analysts: Moving Beyond the Web App
|
||||
|
||||
Once you understand the web app, you can:
|
||||
|
||||
**Use the Python client:**
|
||||
- See `policyengine-python-client-skill` for programmatic access
|
||||
- See `policyengine-us-skill` for detailed simulation patterns
|
||||
|
||||
**Create custom analyses:**
|
||||
- See `policyengine-analysis-skill` for analysis patterns
|
||||
- See `microdf-skill` for data analysis utilities
|
||||
|
||||
**Access the API directly:**
|
||||
- See `policyengine-api-skill` for API documentation
|
||||
- REST endpoints for integration
|
||||
|
||||
## For Contributors: Building PolicyEngine
|
||||
|
||||
To contribute to PolicyEngine development:
|
||||
|
||||
**Understanding the stack:**
|
||||
- See `policyengine-core-skill` for engine architecture
|
||||
- See `policyengine-us-skill` for country model patterns
|
||||
- See `policyengine-api-skill` for API development
|
||||
- See `policyengine-app-skill` for app development
|
||||
|
||||
**Development standards:**
|
||||
- See `policyengine-standards-skill` for code quality requirements
|
||||
- See `policyengine-writing-skill` for documentation style
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
### How accurate is PolicyEngine?
|
||||
|
||||
PolicyEngine uses official tax and benefit rules from legislation and regulations. Calculations match official calculators (IRS, SSA, etc.) for individual households.
|
||||
|
||||
Population-level estimates use microsimulation with survey data (Current Population Survey for US, Family Resources Survey for UK).
|
||||
|
||||
### Can I use PolicyEngine for my taxes?
|
||||
|
||||
PolicyEngine is for policy analysis, not tax filing. Results are estimates based on the information you provide. For filing taxes, use IRS.gov or professional tax software.
|
||||
|
||||
### How is PolicyEngine funded?
|
||||
|
||||
PolicyEngine is a nonprofit funded by grants and donations. The platform is free to use.
|
||||
|
||||
### Can I export results?
|
||||
|
||||
Yes! Charts can be downloaded as PNG or HTML. You can also share reform URLs with others.
|
||||
|
||||
### What programs does PolicyEngine model?
|
||||
|
||||
**US (federal):**
|
||||
- Income tax, payroll tax, capital gains tax
|
||||
- EITC, CTC, ACTC
|
||||
- SNAP, WIC, ACA premium tax credits
|
||||
- Social Security, SSI, TANF
|
||||
- State income taxes (varies by state)
|
||||
|
||||
**UK:**
|
||||
- Income tax, National Insurance
|
||||
- Universal Credit, Child Benefit
|
||||
- State Pension, Pension Credit
|
||||
- Council Tax, Council Tax Support
|
||||
|
||||
For complete lists, see:
|
||||
- US: https://policyengine.org/us/parameters
|
||||
- UK: https://policyengine.org/uk/parameters
|
||||
|
||||
### How do I report a bug?
|
||||
|
||||
**If you find incorrect calculations:**
|
||||
1. Go to the household calculator
|
||||
2. Note your inputs and the incorrect result
|
||||
3. File an issue: https://github.com/PolicyEngine/policyengine-us/issues (or appropriate country repo)
|
||||
4. Include the household URL
|
||||
|
||||
**If you find app bugs:**
|
||||
1. Note what you were doing
|
||||
2. File an issue: https://github.com/PolicyEngine/policyengine-app/issues
|
||||
|
||||
## Resources
|
||||
|
||||
- **Website:** https://policyengine.org
|
||||
- **Documentation:** https://policyengine.org/us/docs
|
||||
- **Blog:** https://policyengine.org/us/research
|
||||
- **GitHub:** https://github.com/PolicyEngine
|
||||
- **Contact:** hello@policyengine.org
|
||||
|
||||
## Related Skills
|
||||
|
||||
- **policyengine-python-client-skill** - Using PolicyEngine programmatically
|
||||
- **policyengine-us-skill** - Understanding US tax/benefit calculations
|
||||
- **policyengine-analysis-skill** - Creating custom policy analyses
|
||||
526
skills/documentation/policyengine-writing-skill/SKILL.md
Normal file
526
skills/documentation/policyengine-writing-skill/SKILL.md
Normal file
@@ -0,0 +1,526 @@
|
||||
---
|
||||
name: policyengine-writing
|
||||
description: PolicyEngine writing style for blog posts, documentation, PR descriptions, and research reports - emphasizing active voice, quantitative precision, and neutral tone
|
||||
---
|
||||
|
||||
# PolicyEngine Writing Skill
|
||||
|
||||
Use this skill when writing blog posts, documentation, PR descriptions, research reports, or any public-facing PolicyEngine content.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Writing blog posts about policy analysis
|
||||
- Creating PR descriptions
|
||||
- Drafting documentation
|
||||
- Writing research reports
|
||||
- Composing social media posts
|
||||
- Creating newsletters
|
||||
- Writing README files
|
||||
|
||||
## Core Principles
|
||||
|
||||
PolicyEngine's writing emphasizes clarity, precision, and objectivity.
|
||||
|
||||
1. **Active voice** - Prefer active constructions over passive
|
||||
2. **Direct and quantitative** - Use specific numbers, avoid vague adjectives/adverbs
|
||||
3. **Sentence case** - Use sentence case for headings, not title case
|
||||
4. **Neutral tone** - Describe what policies do, not whether they're good or bad
|
||||
5. **Precise language** - Choose exact verbs over vague modifiers
|
||||
|
||||
## Active Voice
|
||||
|
||||
Active voice makes writing clearer and more direct.
|
||||
|
||||
**✅ Correct (Active):**
|
||||
```
|
||||
Harris proposes expanding the Earned Income Tax Credit
|
||||
The reform reduces poverty by 3.2%
|
||||
PolicyEngine projects higher costs than other organizations
|
||||
We estimate the ten-year costs
|
||||
The bill lowers the state's top income tax rate
|
||||
Montana raises the EITC from 10% to 20%
|
||||
```
|
||||
|
||||
**❌ Wrong (Passive):**
|
||||
```
|
||||
The Earned Income Tax Credit is proposed to be expanded by Harris
|
||||
Poverty is reduced by 3.2% by the reform
|
||||
Higher costs are projected by PolicyEngine
|
||||
The ten-year costs are estimated
|
||||
The state's top income tax rate is lowered by the bill
|
||||
The EITC is raised from 10% to 20% by Montana
|
||||
```
|
||||
|
||||
## Quantitative and Precise
|
||||
|
||||
Replace vague modifiers with specific numbers and measurements.
|
||||
|
||||
**✅ Correct (Quantitative):**
|
||||
```
|
||||
Costs the state $245 million
|
||||
Benefits 77% of Montana residents
|
||||
Lowers the Supplemental Poverty Measure by 0.8%
|
||||
Raises net income by $252 in 2026
|
||||
The reform affects 14.3 million households
|
||||
Hours worked falls by 0.27%, or 411,000 full-time equivalent jobs
|
||||
The top decile receives an average benefit of $1,033
|
||||
PolicyEngine projects costs 40% higher than the Tax Foundation
|
||||
```
|
||||
|
||||
**❌ Wrong (Vague adjectives/adverbs):**
|
||||
```
|
||||
Significantly costs the state
|
||||
Benefits most Montana residents
|
||||
Greatly lowers poverty
|
||||
Substantially raises net income
|
||||
The reform affects many households
|
||||
Hours worked falls considerably
|
||||
High earners receive large benefits
|
||||
PolicyEngine projects much higher costs
|
||||
```
|
||||
|
||||
## Sentence Case for Headings
|
||||
|
||||
Use sentence case (capitalize only the first word and proper nouns) for all headings.
|
||||
|
||||
**✅ Correct (Sentence case):**
|
||||
```
|
||||
## The proposal
|
||||
## Nationwide impacts
|
||||
## Household impacts
|
||||
## Statewide impacts 2026
|
||||
## Case study: the End Child Poverty Act
|
||||
## Key findings
|
||||
```
|
||||
|
||||
**❌ Wrong (Title case):**
|
||||
```
|
||||
## The Proposal
|
||||
## Nationwide Impacts
|
||||
## Household Impacts
|
||||
## Statewide Impacts 2026
|
||||
## Case Study: The End Child Poverty Act
|
||||
## Key Findings
|
||||
```
|
||||
|
||||
## Neutral, Objective Tone
|
||||
|
||||
Describe what policies do without value judgments. Let readers draw their own conclusions from the data.
|
||||
|
||||
**✅ Correct (Neutral):**
|
||||
```
|
||||
The reform reduces poverty by 3.2% and raises inequality by 0.16%
|
||||
Single filers with earnings between $8,000 and $37,000 see their net incomes increase
|
||||
The tax changes raise the net income of 75.9% of residents
|
||||
PolicyEngine projects higher costs than other organizations
|
||||
The top income decile receives 42% of total benefits
|
||||
```
|
||||
|
||||
**❌ Wrong (Value judgments):**
|
||||
```
|
||||
The reform successfully reduces poverty by 3.2% but unfortunately raises inequality
|
||||
Low-income workers finally see their net incomes increase
|
||||
The tax changes benefit most residents
|
||||
PolicyEngine provides more accurate cost estimates
|
||||
The wealthiest households receive a disproportionate share of benefits
|
||||
```
|
||||
|
||||
## Precise Verbs Over Adverbs
|
||||
|
||||
Choose specific verbs instead of generic verbs modified by adverbs.
|
||||
|
||||
**✅ Correct (Precise verbs):**
|
||||
```
|
||||
The bill lowers the top rate from 5.9% to 5.4%
|
||||
The policy raises the maximum credit from $632 to $1,774
|
||||
The reform increases the phase-in rate from 7.65% to 15.3%
|
||||
This doubles Montana's EITC from 10% to 20%
|
||||
The change eliminates the age cap
|
||||
```
|
||||
|
||||
**❌ Wrong (Vague verbs + adverbs):**
|
||||
```
|
||||
The bill significantly changes the top rate
|
||||
The policy substantially increases the maximum credit
|
||||
The reform greatly boosts the phase-in rate
|
||||
This dramatically expands Montana's EITC
|
||||
The change completely removes the age cap
|
||||
```
|
||||
|
||||
## Concrete Examples
|
||||
|
||||
Always include specific household examples with precise numbers.
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
For a single adult with no children and $10,000 of earnings, the tax provisions
|
||||
increase their net income by $69 in 2026 and $68 in 2027, solely from the
|
||||
doubled EITC match.
|
||||
|
||||
A single parent of two kids with an annual income of $50,000 will see a $252
|
||||
increase to their net income: $179 from the expanded EITC, and $73 from the
|
||||
lower bracket threshold.
|
||||
|
||||
A married couple with no dependents and $200,000 of earnings will see their
|
||||
liability drop by $1,306 in 2027.
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
Low-income workers see modest increases to their net income from the
|
||||
expanded EITC.
|
||||
|
||||
Families with children benefit substantially from the tax changes.
|
||||
|
||||
High earners also see significant reductions in their tax liability.
|
||||
```
|
||||
|
||||
## Tables and Data
|
||||
|
||||
Use tables liberally to present data clearly. Always include units and context.
|
||||
|
||||
**Example 1: Tax parameters over time**
|
||||
|
||||
| Year | Phase-in rate | Max credit | Phase-out start | Phase-out rate |
|
||||
| ---- | ------------- | ---------- | --------------- | -------------- |
|
||||
| 2025 | 15.3% | $1,774 | $13,706 | 15.3% |
|
||||
| 2026 | 15.3% | $1,815 | $14,022 | 15.3% |
|
||||
| 2027 | 15.3% | $1,852 | $14,306 | 15.3% |
|
||||
|
||||
**Example 2: Household impacts**
|
||||
|
||||
| Household composition | 2026 net income change | 2027 net income change |
|
||||
| ------------------------------ | ---------------------- | ---------------------- |
|
||||
| Single, no children, $10,000 | $66 | $68 |
|
||||
| Single, two children, $50,000 | $252 | $266 |
|
||||
| Married, no children, $200,000 | $853 | $1,306 |
|
||||
|
||||
**Example 3: Ten-year costs**
|
||||
|
||||
| Year | Federal cost ($ billions) |
|
||||
| ------- | ------------------------- |
|
||||
| 2025 | 14.3 |
|
||||
| 2026 | 14.4 |
|
||||
| 2027 | 14.7 |
|
||||
| 2025-34 | 143.7 |
|
||||
|
||||
## Avoid Superlatives
|
||||
|
||||
Replace superlative claims with specific comparisons.
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
PolicyEngine projects costs 40% higher than the Tax Foundation
|
||||
The top decile receives an average benefit of $1,033
|
||||
The reform reduces child poverty by 3.2 percentage points
|
||||
This represents Montana's largest income tax cut since 2021
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
PolicyEngine provides the most accurate cost projections
|
||||
The wealthiest households receive massive benefits
|
||||
The reform dramatically slashes child poverty
|
||||
This is Montana's largest income tax cut in history
|
||||
```
|
||||
|
||||
## Structure and Organization
|
||||
|
||||
Follow a clear hierarchical structure with key findings up front.
|
||||
|
||||
**Standard blog post structure:**
|
||||
|
||||
```markdown
|
||||
# Title (H1)
|
||||
|
||||
Opening paragraph states what happened and when, with a link to PolicyEngine.
|
||||
|
||||
Key results in [year]:
|
||||
- Cost: $245 million
|
||||
- Benefits: 77% of residents
|
||||
- Poverty impact: Reduces SPM by 0.8%
|
||||
- Inequality impact: Raises Gini by 0.16%
|
||||
|
||||
## The proposal (H2)
|
||||
|
||||
Detailed description of the policy changes, often with a table showing
|
||||
the specific parameter values.
|
||||
|
||||
## Household impacts (H2)
|
||||
|
||||
Specific examples for representative household types.
|
||||
|
||||
### Example 1: Single filer (H3)
|
||||
Detailed calculation...
|
||||
|
||||
### Example 2: Family with children (H3)
|
||||
Detailed calculation...
|
||||
|
||||
## Statewide impacts (H2)
|
||||
|
||||
Population-level analysis with charts and tables.
|
||||
|
||||
### Budgetary impact (H3)
|
||||
Cost/revenue estimates...
|
||||
|
||||
### Distributional impact (H3)
|
||||
Winners/losers by income decile...
|
||||
|
||||
### Poverty and inequality (H3)
|
||||
Impact on poverty rates and inequality measures...
|
||||
|
||||
## Methodology (H2)
|
||||
|
||||
Explanation of data sources, modeling approach, and caveats.
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Opening Paragraphs
|
||||
|
||||
State the facts directly with dates, actors, and actions:
|
||||
|
||||
```
|
||||
[On April 28, 2025], Governor Greg Gianforte (R-MT) signed House Bill 337,
|
||||
a bill that amends Montana's individual income tax code.
|
||||
|
||||
Vice President Harris proposes expanding the Earned Income Tax Credit (EITC)
|
||||
for filers without qualifying dependents.
|
||||
|
||||
In her economic plan, Harris proposes to restore the expanded Earned Income
|
||||
Tax Credit for workers without children to its level under the American
|
||||
Rescue Plan Act in 2021.
|
||||
```
|
||||
|
||||
### Key Findings Format
|
||||
|
||||
Lead with bullet points of quantitative results:
|
||||
|
||||
```
|
||||
Key results in 2027:
|
||||
- Costs the state $245 million
|
||||
- Benefits 77% of Montana residents
|
||||
- Lowers the Supplemental Poverty Measure by 0.8%
|
||||
- Raises the Gini index by 0.16%
|
||||
```
|
||||
|
||||
### Methodological Transparency
|
||||
|
||||
Always specify the model, version, and assumptions:
|
||||
|
||||
```
|
||||
Based on static microsimulation modeling with PolicyEngine US (version 1.103.0),
|
||||
we project the following economic impacts for 2025.
|
||||
|
||||
Assuming no behavioral responses, we project that the EITC expansion will cost
|
||||
the federal government $14.3 billion in 2025.
|
||||
|
||||
Incorporating elasticities of labor supply used by the Congressional Budget Office
|
||||
increases the reform's cost.
|
||||
|
||||
Over the ten-year budget window, this amounts to $143.7 billion.
|
||||
```
|
||||
|
||||
### Household Examples
|
||||
|
||||
Always include the household composition, income, and specific dollar impacts:
|
||||
|
||||
```
|
||||
For a single adult with no children and $10,000 of earnings, the tax provisions
|
||||
increase their net income by $69 in 2026 and $68 in 2027.
|
||||
|
||||
A single parent of two kids with an annual income of $50,000 will see a $252
|
||||
increase to their net income due to House Bill 337: $179 from the expanded EITC,
|
||||
and $73 from the lower bracket threshold.
|
||||
```
|
||||
|
||||
## Examples in Context
|
||||
|
||||
### Blog Post Opening
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
On April 28, 2025, Governor Gianforte signed House Bill 337, which lowers
|
||||
Montana's top income tax rate from 5.9% to 5.4% and doubles the state EITC
|
||||
from 10% to 20% of the federal credit.
|
||||
|
||||
Key results in 2027:
|
||||
- Costs the state $245 million
|
||||
- Benefits 77% of Montana residents
|
||||
- Lowers the Supplemental Poverty Measure by 0.8%
|
||||
- Raises the Gini index by 0.16%
|
||||
|
||||
Use PolicyEngine to view the full results or calculate the effect on your
|
||||
household.
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
On April 28, 2025, Governor Gianforte made history by signing an amazing new
|
||||
tax cut bill that will dramatically help Montana families. House Bill 337
|
||||
significantly reduces tax rates and greatly expands the EITC.
|
||||
|
||||
This groundbreaking reform will:
|
||||
- Cost the state money
|
||||
- Help most residents
|
||||
- Reduce poverty substantially
|
||||
- Impact inequality
|
||||
|
||||
Check out PolicyEngine to see how much you could save!
|
||||
```
|
||||
|
||||
### PR Description
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
## Summary
|
||||
|
||||
This PR adds Claude Code plugin configuration to enable automated installation
|
||||
of agents and skills for PolicyEngine development.
|
||||
|
||||
## Changes
|
||||
|
||||
- Add plugin auto-install configuration in .claude/settings.json
|
||||
- Configure auto-install of country-models plugin from PolicyEngine/policyengine-claude
|
||||
|
||||
## Benefits
|
||||
|
||||
- Access to 15 specialized agents
|
||||
- 3 slash commands (/encode-policy, /review-pr, /fix-pr)
|
||||
- 2 skills (policyengine-us-skill, policyengine-standards-skill)
|
||||
|
||||
## Testing
|
||||
|
||||
After merging, team members trust the repo and the plugin auto-installs.
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
## Summary
|
||||
|
||||
This amazing PR adds incredible new Claude Code plugin support that will
|
||||
revolutionize PolicyEngine development!
|
||||
|
||||
## Changes
|
||||
|
||||
- Adds some configuration files
|
||||
- Sets up plugins and stuff
|
||||
|
||||
## Benefits
|
||||
|
||||
- Gets you lots of cool new features
|
||||
- Makes development much easier
|
||||
- Provides great new tools
|
||||
|
||||
## Testing
|
||||
|
||||
Should work great once merged!
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
## Installation
|
||||
|
||||
Install PolicyEngine-US from PyPI:
|
||||
|
||||
```bash
|
||||
pip install policyengine-us
|
||||
```
|
||||
|
||||
This installs version 1.103.0 or later, which includes support for 2025
|
||||
tax parameters.
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
## Installation
|
||||
|
||||
Simply install PolicyEngine-US:
|
||||
|
||||
```bash
|
||||
pip install policyengine-us
|
||||
```
|
||||
|
||||
This will install the latest version with all the newest features!
|
||||
```
|
||||
|
||||
## Special Cases
|
||||
|
||||
### Comparisons to Other Organizations
|
||||
|
||||
State facts neutrally without claiming superiority:
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
PolicyEngine projects higher costs than other organizations when considering
|
||||
behavioral responses.
|
||||
|
||||
| Organization | Cost, 2025-2034 ($ billions) |
|
||||
| ------------------------- | ---------------------------- |
|
||||
| PolicyEngine (static) | 144 |
|
||||
| PolicyEngine (dynamic) | 201 |
|
||||
| Tax Foundation | 157 |
|
||||
| Penn Wharton Budget Model | 135 |
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
PolicyEngine provides more accurate estimates than other organizations.
|
||||
|
||||
Unlike other models that underestimate costs, PolicyEngine correctly accounts
|
||||
for behavioral responses to project a more realistic $201 billion cost.
|
||||
```
|
||||
|
||||
### Discussing Limitations
|
||||
|
||||
Acknowledge limitations directly without hedging:
|
||||
|
||||
**✅ Correct:**
|
||||
```
|
||||
## Caveats
|
||||
|
||||
The Current Population Survey has several limitations for tax microsimulation:
|
||||
|
||||
- Truncates high incomes for privacy, underestimating tax impacts on high earners
|
||||
- Underestimates benefit receipt compared to administrative totals
|
||||
- Reflects 2020 data with 2025 policy parameters
|
||||
- Lacks detail for specific income types (assumes all capital gains are long-term)
|
||||
```
|
||||
|
||||
**❌ Wrong:**
|
||||
```
|
||||
## Caveats
|
||||
|
||||
While our model is highly sophisticated, like all models it has some potential
|
||||
limitations that users should be aware of:
|
||||
|
||||
- The data might not perfectly capture high incomes
|
||||
- Benefits may be slightly underestimated
|
||||
- We do our best to extrapolate older data to current years
|
||||
```
|
||||
|
||||
## Writing Checklist
|
||||
|
||||
Before publishing, verify:
|
||||
|
||||
- [ ] Use active voice throughout
|
||||
- [ ] Include specific numbers for all claims
|
||||
- [ ] Use sentence case for all headings
|
||||
- [ ] Maintain neutral, objective tone
|
||||
- [ ] Choose precise verbs over vague adverbs
|
||||
- [ ] Include concrete household examples
|
||||
- [ ] Present data in tables
|
||||
- [ ] Avoid all superlatives
|
||||
- [ ] Structure with clear hierarchy
|
||||
- [ ] Open with key quantitative findings
|
||||
- [ ] Specify model version and assumptions
|
||||
- [ ] Link to PolicyEngine when relevant
|
||||
- [ ] Acknowledge limitations directly
|
||||
|
||||
## Resources
|
||||
|
||||
- **Example posts**: See `policyengine-app/src/posts/articles/` for reference implementations
|
||||
- **PolicyEngine app**: https://policyengine.org for linking to analyses
|
||||
- **Microsimulation docs**: https://policyengine.org/us/docs for methodology details
|
||||
Reference in New Issue
Block a user