acx.code.assistant
Generate code scaffolds following Carbon ACX conventions for React components, TypeScript APIs, Cloudflare Workers, Python scripts, and tests.
SKILL.md
Full skill instructions
acx.code.assistant
Purpose
This skill enables Claude to generate production-ready code for Carbon ACX that adheres to repository conventions, architectural patterns, and quality standards defined in AGENTS.md, CLAUDE.md, and CONTRIBUTING.md.
Capabilities:
- Generate React components with proper TypeScript typing
- Create Cloudflare Worker endpoints with runtime constraints
- Scaffold Python data pipeline scripts
- Generate test files (Vitest, pytest)
- Create typed API client code
- Follow monorepo structure (pnpm workspaces)
- NEW: Generate 3D visualization components with Three.js/React Three Fiber
- NEW: Create 2D overlay components (modals, panels) for transparency
- NEW: Build Apache ECharts visualizations with design tokens
Quality Enforcement:
- All code passes linters (eslint, prettier, ruff, black)
- Includes proper error handling
- TypeScript strict mode compliance
- Includes basic tests
- Follows AGENTS.md review gates
- NEW: Uses design tokens (CSS custom properties)
- NEW: Follows 3D+2D hybrid architecture (viz in canvas, transparency in overlays)
When to Use
Trigger Patterns:
- "Create a React component for displaying emission trends"
- "Generate a Cloudflare Worker endpoint for /api/emissions"
- "Scaffold a Python script to process activity data"
- "Write a test for the EmissionCalculator class"
- "Create a typed API client for the carbon data service"
- "Generate boilerplate for a new layer in the Dash app"
- NEW: "Create a 3D sphere visualization component for..."
- NEW: "Build a citation panel modal for emission factor sources"
- NEW: "Generate an ECharts visualization for timeline data"
- NEW: "Create a Zustand store slice for..."
Do NOT Use When:
- User asks analytical questions (use
carbon.data.qa) - User wants reports (use
carbon.report.gen) - User wants to validate schemas (use
schema.linter) - Modifying core architecture (requires human design review first)
Allowed Tools
read_file- Read existing code for context and patternswrite_file- Create new code filesedit_file- Modify existing files (with caution)bash- Run linters, formatters, tests to verify code quality
Access Level: 2 (File Modification - can create/edit code files with human review)
Tool Rationale:
read_file: Required to understand existing patterns and conventionswrite_file: Required to generate new code filesedit_file: Allowed for targeted edits (must preserve existing logic)bash: Needed to run quality checks (eslint, pytest, etc.)
Explicitly Denied:
- No direct deployment or production changes
- No modifications to
wrangler.toml,.github/workflows/,Makefilewithout explicit user approval (high-risk per AGENTS.md) - No committing or pushing code (human must review first)
Expected I/O
Input:
- Type: Code generation request
- Format: Natural language description of desired code
- Required Context:
- What to build (component, endpoint, script, test)
- Where it fits (file path or module)
- Key functionality
- Optional Context:
- Specific technologies/libraries
- Integration points
- Edge cases to handle
Example:
"Create a React component that displays a bar chart of emissions by layer using our existing chart library"
Output:
- Type: Code file(s) with proper structure
- Format: TypeScript (.tsx, .ts), Python (.py), or JavaScript (.js)
- Location: Appropriate directory per repo structure
- Includes:
- Proper imports
- TypeScript types/interfaces
- Error handling
- JSDoc/docstring comments
- Basic test file (separate)
- Validation:
- Runs through linter without errors
- Follows naming conventions
- Includes type safety
- No TODOs without tracking
Dependencies
Required:
- Access to Carbon ACX repository structure
- Understanding of AGENTS.md conventions
- Knowledge of tech stack:
- TypeScript 5.5+
- React 18
- Vite 5
- Python 3.11+
- Cloudflare Workers runtime
- Reference files:
reference/conventions.md- Coding standardsAGENTS.md- AI development guidelinesCLAUDE.md- Repository-specific rules
Code Quality Tools:
- ESLint + Prettier (TypeScript/JavaScript)
- Ruff + Black (Python)
- Vitest (JavaScript tests)
- pytest (Python tests)
Examples
Example 1: React Component Generation
User: "Create a React component for displaying a list of activities with their emission factors"
Claude Process:
- Read existing component patterns (e.g., from
apps/carbon-acx-web/src/components/) - Check conventions in
reference/conventions.md - Generate component with:
- Proper TypeScript types
- Props interface
- Error handling
- Accessibility attributes
- Generate companion test file
- Run prettier/eslint to verify
- Save to appropriate directory
Output File: apps/carbon-acx-web/src/components/ActivityList.tsx
import React from 'react';
interface Activity {
activity_id: string;
name: string;
emission_factor?: number;
unit?: string;
}
interface ActivityListProps {
activities: Activity[];
onActivitySelect?: (activity: Activity) => void;
}
/**
* Displays a list of activities with their emission factors
* @param activities - Array of activity objects
* @param onActivitySelect - Optional callback when activity is clicked
*/
export const ActivityList: React.FC<ActivityListProps> = ({
activities,
onActivitySelect,
}) => {
if (!activities || activities.length === 0) {
return <p className="text-gray-500">No activities available</p>;
}
return (
<ul className="space-y-2" role="list">
{activities.map((activity) => (
<li
key={activity.activity_id}
className="p-4 border rounded hover:bg-gray-50 cursor-pointer"
onClick={() => onActivitySelect?.(activity)}
role="listitem"
>
<div className="font-semibold">{activity.name}</div>
{activity.emission_factor && (
<div className="text-sm text-gray-600">
{activity.emission_factor} kgCO2e/{activity.unit || 'unit'}
</div>
)}
</li>
))}
</ul>
);
};
Test File: apps/carbon-acx-web/src/components/ActivityList.test.tsx
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ActivityList } from './ActivityList';
describe('ActivityList', () => {
const mockActivities = [
{ activity_id: 'TEST.1', name: 'Test Activity', emission_factor: 1.5, unit: 'hour' },
];
it('renders activities correctly', () => {
render(<ActivityList activities={mockActivities} />);
expect(screen.getByText('Test Activity')).toBeInTheDocument();
expect(screen.getByText(/1.5 kgCO2e/)).toBeInTheDocument();
});
it('shows message when no activities', () => {
render(<ActivityList activities={[]} />);
expect(screen.getByText(/No activities available/)).toBeInTheDocument();
});
});
Example 2: Cloudflare Worker Endpoint
User: "Create a Cloudflare Worker endpoint at /api/layers that returns all layer data"
Claude Process:
- Read existing Worker code in
workers/compute/index.ts - Understand Cloudflare runtime constraints (no Node.js APIs)
- Generate endpoint with:
- Proper error handling
- CORS headers
- Input validation
- Response typing
- Note binding requirements in comments
Output File: workers/compute/routes/layers.ts
/**
* GET /api/layers
* Returns all carbon accounting layers
*
* Runtime: Cloudflare Workers (no Node.js APIs)
* Bindings: None required
*/
interface Layer {
layer_id: string;
layer_name: string;
layer_type: string;
description: string;
}
export async function handleLayersRequest(request: Request): Promise<Response> {
// Validate request method
if (request.method !== 'GET') {
return new Response(
JSON.stringify({ error: 'Method not allowed' }),
{ status: 405, headers: { 'Content-Type': 'application/json' } }
);
}
try {
// In production, this would load from KV or R2
// For now, return static demo data
const layers: Layer[] = [
{
layer_id: 'professional',
layer_name: 'Professional Services',
layer_type: 'civilian',
description: 'Professional and consumer activities',
},
{
layer_id: 'online',
layer_name: 'Digital Infrastructure',
layer_type: 'civilian',
description: 'Online services and digital operations',
},
// Add more layers...
];
return new Response(JSON.stringify({ layers, count: layers.length }), {
status: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*', // Adjust for production
'Cache-Control': 'public, max-age=3600',
},
});
} catch (error) {
console.error('Error fetching layers:', error);
return new Response(
JSON.stringify({ error: 'Internal server error' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
Integration Note: Add route to main Worker in workers/compute/index.ts:
import { handleLayersRequest } from './routes/layers';
// In fetch handler:
if (url.pathname === '/api/layers') {
return handleLayersRequest(request);
}
Example 3: Python Data Processing Script
User: "Create a Python script to aggregate emission factors by layer"
Output: scripts/aggregate_by_layer.py
"""
Aggregate emission factors by layer.
Reads activities.csv and emission_factors.csv, then produces a summary
of total emission factors grouped by layer_id.
"""
import pandas as pd
from pathlib import Path
def aggregate_by_layer(data_dir: Path = Path("data")) -> pd.DataFrame:
"""
Aggregate emission factors by layer.
Args:
data_dir: Path to data directory containing CSV files
Returns:
DataFrame with columns: layer_id, layer_name, total_ef, activity_count
Raises:
FileNotFoundError: If required CSV files are missing
ValueError: If data validation fails
"""
# Load data
activities_path = data_dir / "activities.csv"
emission_factors_path = data_dir / "emission_factors.csv"
layers_path = data_dir / "layers.csv"
if not activities_path.exists():
raise FileNotFoundError(f"Activities file not found: {activities_path}")
activities = pd.read_csv(activities_path)
emission_factors = pd.read_csv(emission_factors_path)
layers = pd.read_csv(layers_path)
# Merge activities with emission factors
merged = activities.merge(
emission_factors, on="activity_id", how="inner"
)
# Aggregate by layer
aggregated = (
merged.groupby("layer_id")
.agg(
total_ef=("emission_factor_kg", "sum"),
activity_count=("activity_id", "count"),
)
.reset_index()
)
# Add layer names
aggregated = aggregated.merge(
layers[["layer_id", "layer_name"]], on="layer_id", how="left"
)
# Sort by total emission factor descending
aggregated = aggregated.sort_values("total_ef", ascending=False)
return aggregated[["layer_id", "layer_name", "total_ef", "activity_count"]]
def main():
"""CLI entry point."""
result = aggregate_by_layer()
print("\n=== Emission Factors by Layer ===\n")
print(result.to_string(index=False))
print(f"\nTotal activities: {result['activity_count'].sum()}")
print(f"Total emission factors: {result['total_ef'].sum():.2f} kgCO2e\n")
if __name__ == "__main__":
main()
Test File: tests/test_aggregate_by_layer.py
import pytest
import pandas as pd
from pathlib import Path
from scripts.aggregate_by_layer import aggregate_by_layer
def test_aggregate_by_layer(tmp_path):
"""Test layer aggregation with mock data."""
# Create mock CSV files
activities_data = pd.DataFrame({
"activity_id": ["A1", "A2", "A3"],
"layer_id": ["professional", "professional", "online"],
"name": ["Activity 1", "Activity 2", "Activity 3"],
})
ef_data = pd.DataFrame({
"activity_id": ["A1", "A2", "A3"],
"emission_factor_kg": [10.5, 20.3, 15.7],
})
layers_data = pd.DataFrame({
"layer_id": ["professional", "online"],
"layer_name": ["Professional Services", "Digital Infrastructure"],
})
# Save to temp directory
activities_data.to_csv(tmp_path / "activities.csv", index=False)
ef_data.to_csv(tmp_path / "emission_factors.csv", index=False)
layers_data.to_csv(tmp_path / "layers.csv", index=False)
# Run aggregation
result = aggregate_by_layer(data_dir=tmp_path)
# Assertions
assert len(result) == 2
assert "professional" in result["layer_id"].values
assert "online" in result["layer_id"].values
prof_row = result[result["layer_id"] == "professional"].iloc[0]
assert prof_row["total_ef"] == pytest.approx(30.8) # 10.5 + 20.3
assert prof_row["activity_count"] == 2
Limitations
Scope Limitations:
- Cannot make architectural decisions (requires human design)
- Cannot modify deployment configs without approval (per AGENTS.md)
- Cannot bypass review gates for security-sensitive code
- Cannot write code using technologies not in the stack
Code Quality Constraints:
- Must pass linters (may need manual fixes for edge cases)
- TypeScript strict mode compliance (may require additional type definitions)
- Test coverage expectations (basic tests provided, comprehensive coverage requires expansion)
Knowledge Boundaries:
- Code generated based on existing patterns - novel patterns may need review
- Cloudflare Workers runtime constraints must be respected
- Brand/style guidelines from
reference/conventions.mdmust be followed
Validation Criteria
Success Metrics:
- ✅ Code compiles/runs without errors
- ✅ Passes linter (eslint, ruff, prettier, black)
- ✅ TypeScript strict mode compliance
- ✅ Includes proper error handling
- ✅ Has basic test coverage
- ✅ Follows naming conventions from
reference/conventions.md - ✅ Includes JSDoc/docstrings for complex functions
- ✅ No hardcoded secrets or credentials
Quality Checks:
# TypeScript/JavaScript
pnpm run lint # ESLint
pnpm run format # Prettier
pnpm run type-check # TypeScript compiler
# Python
ruff check . # Linting
black --check . # Formatting
pytest tests/ # Tests
Failure Modes:
- ❌ Linter errors → Fix before completing
- ❌ Type errors → Add proper types
- ❌ No tests → Generate basic test file
- ❌ TODOs without issue links → Remove or link
- ❌ Hardcoded values (should be config) → Parameterize
Recovery:
- If linter fails: Run formatter and fix issues
- If types incomplete: Infer from usage or use strict types
- If patterns unclear: Read similar existing code
- If tests fail: Debug and fix before delivery
Related Skills
Composes With:
schema.linter- Validate generated config filescarbon.data.qa- Use to understand data structure before generating data code
Not a Replacement For:
- Human code review (per AGENTS.md)
- Architecture decisions
- Deployment operations
3D Universe Patterns (NEW)
Architecture Overview
3D+2D Hybrid Approach:
- 3D Canvas: Interactive data visualization with Three.js/React Three Fiber
- 2D Overlays: Modals, panels, and forms for transparency and data entry
- Design Tokens: CSS custom properties for consistent styling
- State Management: Single Zustand store (simplified from dual-store pattern)
Component Organization
Component Types:
- System Primitives: Button, Input, Dialog, Transition (Tier 1)
- 3D Visualizations: DataUniverse, CentralSphere, OrbitingActivity (Tier 3)
- 2D Overlays: CitationPanel, MethodologyModal, ActivityManagement (Tier 4)
- Domain Pages: CalculatorPage, InsightsPage, ExplorePage (Tier 4)
Example 4: 3D Visualization Component
User: "Create a component for visualizing activity emissions as orbiting spheres"
Output: apps/carbon-acx-web/src/components/viz/ActivitySphere.tsx
/**
* ActivitySphere - 3D Visualization Component
*
* Renders an individual activity as an orbiting sphere in the DataUniverse.
* Size based on emissions (logarithmic scale), color based on intensity.
*/
import * as React from 'react';
import { useFrame } from '@react-three/fiber';
import { Html } from '@react-three/drei';
import * as THREE from 'three';
interface ActivitySphereProps {
activity: {
id: string;
name: string;
annualEmissions: number; // kg CO₂
category?: string;
};
orbitRadius: number;
orbitSpeed: number;
phaseOffset: number;
onClick?: () => void;
}
export function ActivitySphere({
activity,
orbitRadius,
orbitSpeed,
phaseOffset,
onClick,
}: ActivitySphereProps) {
const meshRef = React.useRef<THREE.Mesh>(null);
const groupRef = React.useRef<THREE.Group>(null);
const [hovered, setHovered] = React.useState(false);
// Calculate size (logarithmic scale)
const size = 0.5 + Math.log10(Math.max(activity.annualEmissions, 1)) * 0.3;
// Calculate color based on intensity
const color = React.useMemo(() => {
const tonnes = activity.annualEmissions / 1000;
if (tonnes < 1) return '#10b981'; // green (low)
if (tonnes < 5) return '#f59e0b'; // amber (moderate)
return '#ef4444'; // red (high)
}, [activity.annualEmissions]);
// Orbital motion animation
useFrame(() => {
if (!groupRef.current) return;
const time = Date.now() * orbitSpeed;
const angle = time + phaseOffset;
const x = Math.cos(angle) * orbitRadius;
const z = Math.sin(angle) * orbitRadius;
const y = Math.sin(time * 2) * 2; // vertical wobble
groupRef.current.position.set(x, y, z);
});
return (
<group ref={groupRef}>
<mesh
ref={meshRef}
onClick={onClick}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
>
<sphereGeometry args={[size, 16, 16]} />
<meshStandardMaterial
color={color}
emissive={color}
emissiveIntensity={hovered ? 0.8 : 0.2}
metalness={0.6}
roughness={0.3}
/>
</mesh>
{/* Hover label */}
{hovered && (
<Html position={[0, size + 0.8, 0]} center>
<div
className="px-3 py-2 rounded-lg pointer-events-none"
style={{
backgroundColor: 'rgba(10, 14, 39, 0.95)',
border: `1px solid ${color}`,
color: 'white',
fontSize: 'var(--font-size-xs)',
whiteSpace: 'nowrap',
}}
>
<div style={{ fontWeight: 600, marginBottom: '4px' }}>
{activity.name}
</div>
<div style={{ opacity: 0.8 }}>
{(activity.annualEmissions / 1000).toFixed(2)}t CO₂/yr
</div>
</div>
</Html>
)}
</group>
);
}
Example 5: 2D Overlay Component
User: "Create a modal for displaying emission factor citations"
Output: apps/carbon-acx-web/src/components/domain/CitationModal.tsx
/**
* CitationModal - 2D Overlay Component
*
* Displays emission factor source information with transparency.
* Uses Radix Dialog for accessibility.
*/
import * as React from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { X, ExternalLink } from 'lucide-react';
import { Button } from '../system/Button';
interface Citation {
activityId: string;
activityName: string;
emissionFactor: number;
unit: string;
source: string;
sourceUrl?: string;
methodology?: string;
lastUpdated?: string;
}
interface CitationModalProps {
citation: Citation | null;
open: boolean;
onClose: () => void;
}
export function CitationModal({ citation, open, onClose }: CitationModalProps) {
if (!citation) return null;
return (
<Dialog.Root open={open} onOpenChange={onClose}>
<Dialog.Portal>
<Dialog.Overlay
className="fixed inset-0 z-50"
style={{
backgroundColor: 'rgba(0, 0, 0, 0.6)',
backdropFilter: 'blur(4px)',
}}
/>
<Dialog.Content
className="fixed top-[50%] left-[50%] z-50 max-h-[85vh] w-[90vw] max-w-[600px] translate-x-[-50%] translate-y-[-50%] rounded-[var(--radius-xl)] p-[var(--space-6)] shadow-xl overflow-y-auto"
style={{
backgroundColor: 'var(--surface-elevated)',
border: '1px solid var(--border-default)',
}}
>
{/* Header */}
<div className="flex items-start justify-between mb-[var(--space-6)]">
<Dialog.Title
className="font-bold"
style={{
fontSize: 'var(--font-size-2xl)',
color: 'var(--text-primary)',
}}
>
Emission Factor Source
</Dialog.Title>
<Dialog.Close asChild>
<button
className="p-[var(--space-2)] rounded-[var(--radius-md)]"
style={{ color: 'var(--text-tertiary)' }}
>
<X className="w-5 h-5" />
</button>
</Dialog.Close>
</div>
{/* Activity Info */}
<div className="space-y-[var(--space-4)]">
<div>
<div
className="font-semibold mb-[var(--space-1)]"
style={{
fontSize: 'var(--font-size-sm)',
color: 'var(--text-tertiary)',
}}
>
Activity
</div>
<div style={{ color: 'var(--text-primary)' }}>
{citation.activityName}
</div>
</div>
<div>
<div
className="font-semibold mb-[var(--space-1)]"
style={{
fontSize: 'var(--font-size-sm)',
color: 'var(--text-tertiary)',
}}
>
Emission Factor
</div>
<div
className="font-mono"
style={{
fontSize: 'var(--font-size-lg)',
color: 'var(--text-primary)',
}}
>
{citation.emissionFactor} kg CO₂e/{citation.unit}
</div>
</div>
<div>
<div
className="font-semibold mb-[var(--space-1)]"
style={{
fontSize: 'var(--font-size-sm)',
color: 'var(--text-tertiary)',
}}
>
Source
</div>
<div style={{ color: 'var(--text-primary)' }}>
{citation.source}
</div>
{citation.sourceUrl && (
<a
href={citation.sourceUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-[var(--space-1)] mt-[var(--space-2)] text-[var(--font-size-sm)]"
style={{ color: 'var(--interactive-primary)' }}
>
View source <ExternalLink className="w-4 h-4" />
</a>
)}
</div>
{citation.methodology && (
<div>
<div
className="font-semibold mb-[var(--space-1)]"
style={{
fontSize: 'var(--font-size-sm)',
color: 'var(--text-tertiary)',
}}
>
Methodology
</div>
<div
style={{
fontSize: 'var(--font-size-sm)',
color: 'var(--text-secondary)',
}}
>
{citation.methodology}
</div>
</div>
)}
</div>
{/* Actions */}
<div className="flex justify-end mt-[var(--space-6)] pt-[var(--space-4)] border-t" style={{ borderColor: 'var(--border-subtle)' }}>
<Button variant="primary" onClick={onClose}>
Close
</Button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
Design Token Usage
Always use design tokens for styling:
// ✅ Correct - Using design tokens
<div className="text-[var(--font-size-lg)] text-[var(--text-primary)]">
// ❌ Wrong - Hardcoded values
<div className="text-lg text-gray-900">
// ✅ Correct - Using token-based spacing
<div className="space-y-[var(--space-4)]">
// ❌ Wrong - Hardcoded spacing
<div className="space-y-4">
Available Design Tokens:
- Typography:
--font-size-xsthrough--font-size-5xl(Major Third scale 1.250) - Colors:
--carbon-low,--carbon-moderate,--carbon-high,--carbon-neutral - Story colors:
--color-goal,--color-baseline,--color-improvement,--color-insight - Spacing:
--space-1through--space-16(4px base) - Shadows:
--shadow-sm,--shadow-md,--shadow-lg - Radii:
--radius-sm,--radius-md,--radius-lg,--radius-xl - Motion:
--motion-story-duration(600ms),--motion-story-ease
State Management Pattern
Zustand for all app state:
import { useAppStore } from '../../hooks/useAppStore';
// Get state
const activities = useAppStore((state) => state.activities);
const totalEmissions = useAppStore((state) => state.getTotalEmissions());
// Update state
const { addActivity, removeActivity } = useAppStore();
// Store example structure:
interface AppState {
// Profile data
profile: {
activities: Activity[];
goals: Goal[];
};
// UI state
selectedActivityId: string | null;
// Actions
addActivity: (activity: Activity) => void;
removeActivity: (id: string) => void;
getTotalEmissions: () => number;
}
Maintenance
Owner: ACX Team Review Cycle: Weekly (high-activity skill) Last Updated: 2025-10-27 Version: 2.1.0 (3D Universe architecture update)
Maintenance Notes:
- Update
reference/conventions.mdwhen coding standards change - Review generated code quality monthly
- Keep examples synchronized with actual codebase patterns
- Update when tech stack versions change (React, TypeScript, Python, Three.js)
- NEW: Keep 3D Universe patterns current (Three.js, React Three Fiber, Drei helpers)
- NEW: Update component examples as hybrid 2D+3D architecture evolves
