Developer
Technical Specification
Full engineering blueprint for the Allocate platform.
Engineering Blueprint v0.1.0
Status reviewed August 14, 2026: This blueprint records the intended product architecture. Railway is the selected web target. The hosted endpoint, DNS/TLS, connectors, npm packages, and public writes remain externally unverified until their separately authorized release gates pass. See the current launch status and quickstart before following operational examples.
Table of Contents
- System Overview
- Technology Stack
- Repository Structure
- Data Models & Schemas
- API Specification
- Agent SDK
- CLI Tool
- Web Application
- Session Artifact Materialization
- Vouch Trust System Integration
- Impact Accounting Ledger
- CI/CD Pipeline
- Authentication & Authorization
- Output Validation Engine
- Search & Discovery
- Notification System
- Monitoring & Observability
- Security Considerations
- Performance Requirements
- Deployment Architecture
- Testing Strategy
- Migration & Versioning Strategy
1. System Overview
Architecture Diagram
┌─────────────────────────────────────────────────────────────────────┐
│ USERS │
│ Community Members │ Volunteers │ Agent Contributors │
└────────┬────────────┴──────┬───────┴──────────┬─────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌──────────────┐ ┌───────────────────┐
│ Web App │ │ Mobile App │ │ CLI / Agent SDK │
│ (Next.js) │ │ (React │ │ (Node.js) │
│ shadcn/ui │ │ Native) │ │ │
└────────┬─────────┘ └──────┬───────┘ └───────┬───────────┘
│ │ │
└───────────┬───────┘ │
▼ │
┌───────────────────────┐ │
│ API Layer │ │
│ (Next.js API Routes │◄─────────────┘
│ + Supabase Edge │
│ Functions) │
└───────────┬───────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Supabase │ │ GitHub │
│ (Application │ │ (Source, CI, │
│ Data Store) │ │ Ingestion) │
│ │ │ │
│ - PostgreSQL │ │ - Repos │
│ - Auth │ │ - PRs │
│ - Storage │ │ - Actions │
│ - Realtime │ │ - Webhooks │
└────────┬─────────┘ └─────────────────┘
│
▼
┌─────────────────────────┐
│ Session Artifact │
│ Materialization │
│ - Work logs │
│ - Ledger entries │
└─────────────────────────┘The diagram shows the intended platform shape. The current launch candidate has
the web app and local CLI and agent packages. Mobile support, GitHub ingestion,
and automated synchronization require separate implementation and release
evidence. The GitHub webhook currently returns 410 GITHUB_INGESTION_DISABLED;
see API Routes for current route behavior.
Core Principles
- Explicit runtime authority: Supabase is the application data store; GitHub source, CI, and signed webhook ingestion are separate responsibilities.
- Offline-capable contributor tooling: local artifacts remain reviewable before any authorized sync through scoped contracts.
- Progressive enhancement: every feature works at the Git level first, and the web UI adds a convenience layer.
- Schema-driven: Zod schemas define the data types used by validation, UI generation, and database sync.
- Stateless agents: agents do not maintain state between sessions; all session state lives in the repository.
2. Technology Stack
Core
| Component | Technology | Rationale |
|---|---|---|
| Web Framework | Next.js 16 (App Router) | SSR, API routes, React Server Components |
| UI Library | shadcn/ui + Tailwind CSS | Production-grade, accessible, customizable |
| Database | Supabase (PostgreSQL 15+) | PostGIS, RLS, Realtime, Auth, Edge Functions |
| Source control & CI | GitHub (Git) | Version control, PRs, Actions, Webhooks |
| Authentication | Supabase Auth (GitHub OAuth) | Ties identity to Git contributions |
| File Storage | Supabase Storage + Git LFS | Evidence photos, media assets |
| Search | Supabase Full-Text Search + pg_trgm | Finding search, task search |
| Realtime | Supabase Realtime | Live voting, task claims, notifications |
| Deployment | Railway | Selected Next.js web target; exact-fingerprint delivery is separately authorized |
| CI/CD | GitHub Actions | Validation and package staging; hosted delivery is separate |
| Monitoring | Sentry + Supabase Dashboard | Error tracking, performance |
Agent SDK
| Component | Technology | Rationale |
|---|---|---|
| Runtime | Node.js 20+ | Universal, async-first |
| Package Manager | npm | Standard distribution |
| Schema Validation | Zod | Runtime type checking, YAML validation |
| YAML Parser | yaml (npm) | YAML 1.2 compliant |
| Git Operations | simple-git | Programmatic Git operations |
| CLI Framework | Commander.js | Standard CLI builder |
| API Clients | Provider SDKs (Anthropic, OpenAI, etc.) | Contributors bring their own |
Mobile (Phase 3)
| Component | Technology | Rationale |
|---|---|---|
| Framework | React Native + Expo | Share code with web |
| Navigation | Expo Router | File-based routing |
| Maps | react-native-maps | Task locations, boundaries |
3. Repository Structure
GitHub Organization: allocate
allocate/
├── allocate/ # Main monorepo
│ ├── apps/
│ │ ├── web/ # Next.js web application
│ │ │ ├── app/
│ │ │ │ ├── (marketing)/
│ │ │ │ ├── (app)/
│ │ │ │ │ ├── [neighborhood]/
│ │ │ │ │ │ ├── page.tsx # Dashboard
│ │ │ │ │ │ ├── projects/
│ │ │ │ │ │ ├── domains/
│ │ │ │ │ │ ├── volunteer/
│ │ │ │ │ │ ├── proposals/
│ │ │ │ │ │ ├── nonprofits/
│ │ │ │ │ │ ├── municipal/
│ │ │ │ │ │ ├── impact/
│ │ │ │ │ │ └── settings/
│ │ │ │ │ └── dashboard/
│ │ │ │ └── api/
│ │ │ │ ├── webhooks/github/
│ │ │ │ ├── sync/
│ │ │ │ ├── validate/
│ │ │ │ └── ledger/
│ │ │ ├── components/
│ │ │ │ ├── ui/ # shadcn/ui
│ │ │ │ ├── findings/
│ │ │ │ ├── tasks/
│ │ │ │ ├── proposals/
│ │ │ │ ├── maps/
│ │ │ │ ├── impact/
│ │ │ │ └── governance/
│ │ │ └── lib/
│ │ │ ├── supabase/
│ │ │ ├── github/
│ │ │ ├── sync/
│ │ │ ├── ledger/
│ │ │ └── validation/
│ │ └── mobile/ # React Native (Phase 3)
│ │
│ ├── packages/
│ │ ├── allocate-sdk/ # Core SDK (shared types, validation, schemas)
│ │ │ └── src/
│ │ │ ├── schemas/ # Zod schemas for all output types
│ │ │ ├── types/
│ │ │ ├── validation/
│ │ │ ├── ledger/
│ │ │ └── utils/
│ │ │
│ │ ├── allocate-agent/ # Agent runtime package
│ │ │ └── src/
│ │ │ ├── agent.ts
│ │ │ ├── config.ts
│ │ │ ├── providers/ # anthropic.ts, openai.ts, local.ts
│ │ │ ├── tools/ # MCP tool integrations
│ │ │ ├── output/ # Output formatters
│ │ │ ├── scheduler.ts
│ │ │ └── submit.ts
│ │ │
│ │ └── allocate-cli/ # CLI tool
│ │ └── src/commands/
│ │
│ ├── supabase/
│ │ ├── migrations/
│ │ ├── functions/
│ │ └── seed.sql
│ │
│ ├── .github/workflows/
│ │ ├── validate-pr.yml
│ │ ├── full-gate.yml
│ │ └── publish-packages.yml
│ │
│ ├── docs/
│ │ ├── TECH_SPEC.md
│ │ ├── CONTRIBUTING.md
│ │ ├── AGENT_CONTRACT.md
│ │ └── API.md
│ │
│ ├── turbo.json
│ └── package.json
│
├── neighborhoods/ # Data repos (one per neighborhood)
│ └── (created dynamically)
│
└── .github/profile/README.mdMonorepo: Turborepo with npm workspaces
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**"] },
"dev": { "cache": false, "persistent": true },
"lint": {},
"test": { "dependsOn": ["build"] },
"validate": { "dependsOn": ["build"] }
}
}4. Data Models & Schemas
All data types defined as Zod schemas in packages/allocate-sdk/src/schemas/. These are the single source of truth for validation, TypeScript types, database sync, and API contracts.
Common Types
// schemas/common.ts
import { z } from "zod";
export const ResourceInputsSchema = z.object({
compute: z
.object({
model: z.string(),
provider: z.string(),
region: z.string().optional(),
input_tokens: z.number().int().nonnegative(),
output_tokens: z.number().int().nonnegative(),
api_calls: z.number().int().nonnegative(),
wall_clock_seconds: z.number().nonnegative(),
estimated_cost_usd: z.number().nonnegative(),
})
.optional(),
data_sources_queried: z
.array(
z.object({
source: z.string(),
calls: z.number().int().nonnegative(),
data_transferred_kb: z.number().nonnegative(),
}),
)
.optional(),
human: z
.object({
hours: z.number().positive(),
type: z.enum([
"physical_verification",
"research_review",
"governance_participation",
"code_contribution",
"community_input",
"meeting_attendance",
"documentation",
]),
skills: z.array(z.string()).optional(),
location: z.object({ lat: z.number(), lon: z.number() }).optional(),
travel_method: z
.enum(["walked", "biked", "drove", "transit", "remote"])
.optional(),
travel_distance_km: z.number().nonnegative().optional(),
})
.optional(),
});
export const SourceSchema = z.object({
id: z.string(),
type: z.enum([
"public_record",
"remote_sensing",
"api",
"database",
"document",
"website",
"academic",
"news",
"community",
]),
name: z.string(),
url: z.string().url().optional(),
provider: z.string().optional(),
date: z.string().optional(),
accessed: z.string(),
reliability: z.enum([
"official",
"peer_reviewed",
"journalistic",
"community",
"unknown",
]),
});
export const ConfidenceLevel = z.enum(["high", "medium", "low"]);
export const LegalSensitivity = z.enum(["low", "medium", "high"]);
export const Urgency = z.enum(["low", "medium", "high", "critical"]);
export const Difficulty = z.enum(["easy", "moderate", "hard", "expert"]);
export const DomainSchema = z.enum([
"accessibility",
"housing",
"environmental",
"crime",
"trafficking",
"safety",
"infrastructure",
"education",
"economic",
"health",
"cultural",
"government",
"technology",
"nonprofit",
"disaster",
]);Research Finding
// schemas/finding.ts
export const ResearchFindingSchema = z.object({
type: z.literal("research_finding"),
version: z.string().default("1.0"),
id: z.string().regex(/^rf-\d{4}-\d{2}-\d{2}-\d{3,}$/),
agent: z.object({ model: z.string(), framework: z.string() }),
contributor: z.string(),
neighborhood: z.string(),
project: z.string().optional(),
timestamp: z.string().datetime(),
valid_until: z.string().datetime().optional(),
staleness: z
.string()
.regex(/^\d+d$/)
.default("90d"),
domain: DomainSchema,
tags: z.array(z.string()),
confidence: ConfidenceLevel,
legal_sensitivity: LegalSensitivity.default("low"),
requires_verification: z.boolean().default(false),
sources: z.array(SourceSchema).min(1, "At least one source required"),
resources: ResourceInputsSchema,
data: z.record(z.unknown()).optional(),
// === Dual-output pattern: formal + colloquial ===
// Plain-language summary for residents (REQUIRED)
// Written for someone with no technical or regulatory background.
// Should answer: What's the problem? Where is it? Why does it matter? What can be done?
summary: z.object({
tldr: z.string().max(280), // One-sentence, tweet-length summary
situation: z.string(), // Plain-language description of what was found
impact: z.string(), // Why this matters to residents
recommendation: z.string().optional(), // What could be done about it
}),
// Formal standards assessment (STRONGLY ENCOURAGED)
// Maps findings to specific regulatory/standard clauses for official use.
// Agents should ALWAYS attempt to identify applicable standards.
// Enables: government filings, grant applications, legal complaints,
// cross-neighborhood aggregation, trend analysis
conformity_assessment: z
.object({
standards_applied: z.array(
z.object({
standard_id: z.string(), // e.g. 'ada-2010', 'ca-building-code-11b'
standard_name: z.string(), // e.g. 'ADA Standards for Accessible Design (2010)'
authority: z.string(), // e.g. 'U.S. Department of Justice'
}),
),
findings: z.array(
z.object({
standard_id: z.string(),
clause: z.string(), // e.g. '406.1'
clause_title: z.string(), // e.g. 'Curb Ramps — General'
status: z.enum([
"conforming",
"nonconforming",
"partially_conforming",
"not_assessed",
]),
severity: z
.enum(["observation", "minor", "major", "critical"])
.optional(),
evidence: z.string(), // Specific evidence supporting the assessment
corrective_action: z.string().optional(), // What would bring this into conformity
}),
),
scope_note: z.string().optional(), // What the assessment covers and omits
limitations: z.array(z.string()).optional(), // What requires verification
})
.optional(), // Optional. Agents are prompted to always attempt it.
});Volunteer Task
// schemas/volunteer-task.ts
export const VolunteerTaskSchema = z.object({
type: z.literal("volunteer_task"),
version: z.string().default("1.0"),
id: z.string().regex(/^vt-\d{4}-\d{2}-\d{2}-\d{3,}$/),
neighborhood: z.string(),
project: z.string().optional(),
created_by: z.string(),
related_finding: z.string().optional(),
title: z.string().max(200),
description: z.string(),
instructions: z.array(z.string()),
skills_needed: z.array(z.string()),
equipment_needed: z.array(z.string()).default([]),
estimated_time_hours: z.number().positive(),
location: z.object({
description: z.string(),
lat: z.number(),
lon: z.number(),
radius_km: z.number().optional(),
}),
urgency: Urgency,
difficulty: Difficulty,
verification_type: z.enum([
"photo_evidence",
"measurement_data",
"attendance_confirmation",
"document_submission",
"video_evidence",
"audio_recording",
"survey_completion",
]),
verification_instructions: z.string(),
status: z
.enum([
"open",
"claimed",
"in_progress",
"completed",
"verified",
"expired",
])
.default("open"),
claimed_by: z.string().nullable().default(null),
expires: z.string().datetime(),
});Task Completion
// schemas/task-completion.ts
export const TaskCompletionSchema = z.object({
type: z.literal("task_completion"),
version: z.string().default("1.0"),
id: z.string().regex(/^tc-\d{4}-\d{2}-\d{2}-\d{3,}$/),
volunteer: z.string(),
neighborhood: z.string(),
task_id: z.string(),
timestamp: z.string().datetime(),
duration_hours: z.number().positive(),
evidence: z.array(
z.object({
type: z.enum([
"photo",
"video",
"audio",
"measurement_data",
"document",
"survey",
]),
file: z.string(),
description: z.string(),
}),
),
data: z.record(z.unknown()).optional(),
resources: ResourceInputsSchema,
notes: z.string().optional(),
});Community Proposal
// schemas/proposal.ts
export const CommunityProposalSchema = z.object({
type: z.literal("community_proposal"),
version: z.string().default("1.0"),
id: z.string().regex(/^cp-\d{4}-\d{2}-\d{2}-\d{3,}$/),
author: z.string(),
neighborhood: z.string(),
timestamp: z.string().datetime(),
discussion_period_days: z.number().int().positive().default(7),
vote_deadline: z.string().datetime(),
proposal_type: z.enum([
"policy_recommendation",
"project_initiation",
"resource_request",
"structural_change",
"partnership",
"event",
]),
governance_tier: z.enum([
"research_review",
"community_vote",
"supermajority",
"founder_approval",
]),
supporting_findings: z.array(z.string()).default([]),
status: z.enum(["open", "passed", "rejected", "withdrawn"]).default("open"),
});Agent Work Log
// schemas/work-log.ts
export const WorkLogStepSchema = z.object({
step: z.number().int().positive(),
timestamp: z.string().datetime(),
action: z.enum([
"query_api",
"analysis",
"synthesis",
"validation",
"error_recovery",
"tool_call",
"mcp_call",
"web_search",
"file_read",
"file_write",
]),
target: z.string().optional(),
description: z.string().optional(),
result: z.enum(["success", "failure", "partial", "skipped"]),
tokens: z
.object({ input: z.number().int(), output: z.number().int() })
.optional(),
data_transferred_kb: z.number().nonnegative().optional(),
error: z.string().optional(),
});
export const AgentWorkLogSchema = z.object({
type: z.literal("agent_work_log"),
version: z.string().default("1.0"),
// Legacy decimal worklog ids remain valid; migration 00059 also accepts
// collision-resistant SHA-256 ids for new session materializations.
id: z.string().regex(/^log-\d{4}-\d{2}-\d{2}-(?:\d{3,}|[0-9a-f]{64})$/),
finding_id: z.string().nullable(),
agent: z.object({
model: z.string(),
framework: z.string(),
version: z.string().optional(),
system_prompt_hash: z.string().optional(),
}),
contributor: z.string(),
session: z.object({
start: z.string().datetime(),
end: z.string().datetime(),
total_duration_seconds: z.number().nonnegative(),
status: z.enum(["completed", "failed", "partial", "timeout"]),
}),
steps: z.array(WorkLogStepSchema),
errors: z
.array(
z.object({
step: z.number().int().optional(),
timestamp: z.string().datetime(),
type: z.string(),
message: z.string(),
recovered: z.boolean(),
}),
)
.default([]),
decisions: z.array(z.string()).default([]),
resource_totals: z.object({
total_tokens: z.object({
input: z.number().int(),
output: z.number().int(),
}),
total_api_calls: z.number().int(),
total_data_transferred_kb: z.number(),
total_wall_clock_seconds: z.number(),
estimated_cost_usd: z.number(),
}),
});Database Schema (Supabase/PostgreSQL)
-- Enable extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Neighborhoods
CREATE TABLE neighborhoods (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
slug TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
location GEOGRAPHY(POLYGON, 4326),
centroid GEOGRAPHY(POINT, 4326),
founder_github TEXT NOT NULL,
github_repo TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Findings
CREATE TABLE findings (
id TEXT PRIMARY KEY,
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
project_slug TEXT,
domain TEXT NOT NULL,
title TEXT NOT NULL,
summary TEXT,
body_markdown TEXT,
confidence TEXT NOT NULL CHECK (confidence IN ('high', 'medium', 'low')),
legal_sensitivity TEXT DEFAULT 'low',
requires_verification BOOLEAN DEFAULT FALSE,
verified BOOLEAN DEFAULT FALSE,
verified_by TEXT,
verified_at TIMESTAMPTZ,
valid_until TIMESTAMPTZ,
staleness_days INTEGER DEFAULT 90,
tags TEXT[] DEFAULT '{}',
sources JSONB DEFAULT '[]',
data JSONB,
standards_references JSONB DEFAULT '[]', -- optional formal standard citations
resources JSONB,
contributor_github TEXT NOT NULL,
agent_model TEXT,
commit_sha TEXT,
search_vector TSVECTOR,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Volunteer Tasks
CREATE TABLE volunteer_tasks (
id TEXT PRIMARY KEY,
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
project_slug TEXT,
title TEXT NOT NULL,
description TEXT NOT NULL,
instructions JSONB DEFAULT '[]',
skills_needed TEXT[] DEFAULT '{}',
equipment_needed TEXT[] DEFAULT '{}',
estimated_time_hours NUMERIC NOT NULL,
location GEOGRAPHY(POINT, 4326),
location_description TEXT,
location_radius_km NUMERIC,
urgency TEXT DEFAULT 'medium',
difficulty TEXT DEFAULT 'moderate',
verification_type TEXT NOT NULL,
verification_instructions TEXT,
status TEXT DEFAULT 'open',
claimed_by TEXT,
claimed_at TIMESTAMPTZ,
expires TIMESTAMPTZ NOT NULL,
created_by TEXT NOT NULL,
related_finding TEXT REFERENCES findings(id),
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Task Completions
CREATE TABLE task_completions (
id TEXT PRIMARY KEY,
task_id TEXT REFERENCES volunteer_tasks(id) ON DELETE CASCADE,
volunteer_github TEXT NOT NULL,
duration_hours NUMERIC NOT NULL,
evidence JSONB DEFAULT '[]',
data JSONB,
resources JSONB,
notes TEXT,
commit_sha TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Proposals
CREATE TABLE proposals (
id TEXT PRIMARY KEY,
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
author_github TEXT NOT NULL,
proposal_type TEXT NOT NULL,
governance_tier TEXT NOT NULL,
title TEXT NOT NULL,
body_markdown TEXT,
supporting_findings TEXT[] DEFAULT '{}',
discussion_period_days INTEGER DEFAULT 7,
vote_deadline TIMESTAMPTZ NOT NULL,
status TEXT DEFAULT 'open',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Votes
CREATE TABLE votes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
proposal_id TEXT REFERENCES proposals(id) ON DELETE CASCADE,
voter_github TEXT NOT NULL,
vote TEXT NOT NULL CHECK (vote IN ('for', 'against', 'abstain')),
comment TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(proposal_id, voter_github)
);
-- Vouched Members (see Section 10 for full enhanced schema)
CREATE TABLE vouched_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
github_username TEXT NOT NULL,
trust_tier INTEGER NOT NULL DEFAULT 0 CHECK (trust_tier IN (0, 1, 2)),
status TEXT NOT NULL CHECK (status IN ('pending', 'vouched', 'steward', 'nonconforming', 'nc_pending')),
vouched_by TEXT,
vouch_reason TEXT,
vouch_source TEXT DEFAULT 'direct',
nc_issued_by TEXT,
nc_basis TEXT, -- must cite specific community standard
nc_confirmed_by TEXT[] DEFAULT '{}',
nc_review_deadline TIMESTAMPTZ,
first_contribution_at TIMESTAMPTZ,
merged_findings_count INTEGER DEFAULT 0,
completed_tasks_count INTEGER DEFAULT 0,
commit_sha TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(neighborhood_id, github_username)
);
-- Agent Work Logs
CREATE TABLE agent_work_logs (
id TEXT PRIMARY KEY,
finding_id TEXT REFERENCES findings(id),
contributor_github TEXT NOT NULL,
agent_model TEXT NOT NULL,
agent_framework TEXT,
session_start TIMESTAMPTZ NOT NULL,
session_end TIMESTAMPTZ NOT NULL,
session_status TEXT NOT NULL,
steps JSONB DEFAULT '[]',
errors JSONB DEFAULT '[]',
decisions JSONB DEFAULT '[]',
total_tokens_input INTEGER DEFAULT 0,
total_tokens_output INTEGER DEFAULT 0,
total_api_calls INTEGER DEFAULT 0,
total_data_transferred_kb NUMERIC DEFAULT 0,
total_wall_clock_seconds NUMERIC DEFAULT 0,
estimated_cost_usd NUMERIC DEFAULT 0,
commit_sha TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Ledger Entries
CREATE TABLE ledger_entries (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
source_type TEXT NOT NULL CHECK (source_type IN ('finding', 'task_completion', 'infrastructure')),
source_id TEXT NOT NULL,
period DATE NOT NULL,
contributor_github TEXT,
-- Raw inputs (immutable)
agent_model TEXT,
agent_provider TEXT,
agent_region TEXT,
agent_tokens_input INTEGER DEFAULT 0,
agent_tokens_output INTEGER DEFAULT 0,
agent_compute_seconds NUMERIC DEFAULT 0,
agent_cost_usd NUMERIC DEFAULT 0,
human_hours NUMERIC DEFAULT 0,
human_travel_method TEXT,
human_travel_km NUMERIC DEFAULT 0,
-- Calculated outputs (recalculated with updated factors)
estimated_kwh NUMERIC,
estimated_co2_kg NUMERIC,
conversion_factors_version TEXT,
calculated_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_findings_neighborhood ON findings(neighborhood_id);
CREATE INDEX idx_findings_domain ON findings(domain);
CREATE INDEX idx_findings_confidence ON findings(confidence);
CREATE INDEX idx_findings_search ON findings USING gin(search_vector);
CREATE INDEX idx_findings_title_trgm ON findings USING gin(title gin_trgm_ops);
CREATE INDEX idx_tasks_neighborhood ON volunteer_tasks(neighborhood_id);
CREATE INDEX idx_tasks_status ON volunteer_tasks(status);
CREATE INDEX idx_tasks_location ON volunteer_tasks USING gist(location);
CREATE INDEX idx_proposals_neighborhood ON proposals(neighborhood_id);
CREATE INDEX idx_votes_proposal ON votes(proposal_id);
CREATE INDEX idx_vouched_neighborhood ON vouched_members(neighborhood_id);
CREATE INDEX idx_ledger_neighborhood ON ledger_entries(neighborhood_id);
CREATE INDEX idx_ledger_period ON ledger_entries(period);
-- Full-text search trigger
CREATE OR REPLACE FUNCTION findings_search_trigger() RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english',
COALESCE(NEW.title, '') || ' ' ||
COALESCE(NEW.summary, '') || ' ' ||
COALESCE(NEW.body_markdown, '') || ' ' ||
COALESCE(array_to_string(NEW.tags, ' '), '')
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER findings_search_update
BEFORE INSERT OR UPDATE ON findings
FOR EACH ROW EXECUTE FUNCTION findings_search_trigger();5. API Specification
This section records the blueprint endpoint inventory. Current public catalog
routes live under /api/v1. Current session, findings, validation, sync, and
webhook behavior is documented in API Routes. Verify each
blueprint endpoint against the current route before using it.
Base origin: https://allocateagents.org
All endpoint paths below are relative to that origin.
All authenticated endpoints require: Authorization: Bearer <supabase_access_token>
Neighborhoods
GET /api/neighborhoods # List all
GET /api/neighborhoods/:slug # Detail
POST /api/neighborhoods # Create (auth)
PATCH /api/neighborhoods/:slug # Update (founder only)
GET /api/neighborhoods/:slug/stats # Statistics
GET /api/neighborhoods/:slug/impact # Impact summaryFindings
GET /api/neighborhoods/:slug/findings # List (paginated, filterable)
GET /api/neighborhoods/:slug/findings/:id # Detail
POST /api/neighborhoods/:slug/findings/:id/verify # Mark verified (auth)
GET /api/neighborhoods/:slug/findings/stale # Stale findings
# Query params: ?domain=&confidence=&verified=&stale=&project=&search=&sort=&page=&limit=Volunteer Tasks
GET /api/neighborhoods/:slug/tasks # List (paginated, filterable)
GET /api/neighborhoods/:slug/tasks/:id # Detail
POST /api/neighborhoods/:slug/tasks # Create (auth)
PATCH /api/neighborhoods/:slug/tasks/:id/claim # Claim (auth)
PATCH /api/neighborhoods/:slug/tasks/:id/unclaim # Unclaim (auth)
POST /api/neighborhoods/:slug/tasks/:id/complete # Submit completion (auth)
PATCH /api/neighborhoods/:slug/tasks/:id/verify # Verify completion (reviewer)
# Query params: ?status=&urgency=&skills=&near=lat,lon&radius=&sort=Proposals & Voting
GET /api/neighborhoods/:slug/proposals # List
GET /api/neighborhoods/:slug/proposals/:id # Detail
POST /api/neighborhoods/:slug/proposals # Create (auth)
POST /api/neighborhoods/:slug/proposals/:id/vote # Vote (auth)
GET /api/neighborhoods/:slug/proposals/:id/results # ResultsGovernance / Vouch & Trust
GET /api/neighborhoods/:slug/members # List all members with trust tiers
GET /api/neighborhoods/:slug/members/:username # Member detail + vouch history
POST /api/neighborhoods/:slug/vouch # Vouch for user (Tier 1+, rate limited)
POST /api/neighborhoods/:slug/nonconformity # Issue nonconformity (requires cited standard)
POST /api/neighborhoods/:slug/nonconformity/:id/confirm # Confirm pending NC (Tier 2)
POST /api/neighborhoods/:slug/nonconformity/:id/contest # Contest NC (corrective action request)
POST /api/neighborhoods/:slug/promote # Promote to steward (Tier 2/founder)
GET /api/neighborhoods/:slug/trust-network # Full trust graph data
GET /api/neighborhoods/:slug/trust-policy # Current trust configuration
POST /api/neighborhoods/:slug/staging # Submit unverified finding (Tier 0)
GET /api/neighborhoods/:slug/staging # Browse staged/unverified findingsUser
GET /api/me # Authenticated account/work summaryValidation & Sync
POST /api/validate # Validate admitted JSON output
POST /api/webhooks/github # Fixed 410; ingestion retired
POST /api/sync # Manual service-token sync
POST /api/ledger # Bounded service-token materializationResponse Envelope
{ "ok": true, "data": T, "meta": { "cursor": "...", "has_more": true } }
{ "ok": false, "error": { "code": "NOT_FOUND", "message": "..." } }6. Agent SDK
Package: @allocate/agent
// Core interface — providers implement this
interface AllocateAgent {
config: AgentConfig;
run(options?: RunOptions): Promise<AgentSession>;
research(task: ResearchTask): Promise<ResearchOutput>;
generateFinding(research: ResearchOutput): Promise<ResearchFinding>;
generateWorkLog(session: AgentSession): Promise<AgentWorkLog>;
stage(finding: ResearchFinding, log: AgentWorkLog): Promise<string>;
submit(finding: ResearchFinding, log: AgentWorkLog): Promise<string>;
}
interface AgentConfig {
neighborhood: string;
contributor: string; // github:username
provider: "anthropic" | "openai" | "local" | "custom";
model: string;
apiKeyEnv: string;
maxTokensPerSession: number;
maxCostPerSessionUsd: number;
focusAreas: Domain[];
schedule: {
mode: "daily" | "continuous" | "manual";
time?: string;
timezone?: string;
};
output: { directory: string; autoSubmit: boolean };
}
interface RunOptions {
taskId?: string;
dryRun?: boolean;
maxTasks?: number;
focusOverride?: Domain[];
}Provider Adapter Pattern
// providers/anthropic.ts
export class AnthropicAgent extends AllocateAgentBase {
private client: Anthropic;
async executeResearchStep(prompt: string, tools: Tool[]): Promise<StepResult> {
const response = await this.client.messages.create({
model: this.config.model,
max_tokens: 4096,
system: this.getSystemPrompt(),
messages: [{ role: 'user', content: prompt }],
tools: tools.map(t => t.toAnthropicTool()),
});
this.trackTokens({ input: response.usage.input_tokens, output: response.usage.output_tokens });
if (this.isOverBudget()) throw new BudgetExceededError(...);
return this.parseResponse(response);
}
}MCP Tool Registry
export const ALLOCATE_TOOLS = {
"public-records": { mcpServer: "allocate-public-records", requiredEnv: [] },
"remote-sensing": {
mcpServer: "allocate-remote-sensing",
requiredEnv: ["SENTINEL_API_KEY"],
},
census: { mcpServer: "allocate-census", requiredEnv: ["CENSUS_API_KEY"] },
environmental: { mcpServer: "allocate-environmental", requiredEnv: [] },
accessibility: { mcpServer: "allocate-accessibility", requiredEnv: [] },
nonprofit: {
mcpServer: "allocate-nonprofit",
requiredEnv: ["PROPUBLICA_API_KEY"],
},
municipal: { mcpServer: "allocate-municipal", requiredEnv: [] },
crime: { mcpServer: "allocate-crime", requiredEnv: [] },
};7. CLI Tool
Package: @allocate/cli
The public launch exposes the read-only catalog commands below by default. It does not expose contributor writes or agent orchestration.
allocate <command> [options]
projects list / show <slug> Browse public projects
evidence search [query] Search public evidence
releases show / export <id> Inspect public releases
resources list / show <slug> Browse public resources
mcp stdio Serve the public catalog over stdioLegacy contributor commands are intentionally absent from the public CLI. The public documentation provides no activation walkthrough for that compatibility surface; authenticated contribution uses the reviewed web and scoped MCP contracts and still requires separate public-write authorization.
Configuration: ~/.allocate/config.yaml
user:
github_username: johnny_allocate
auth_token_env: ALLOCATE_AUTH_TOKEN
neighborhoods:
- slug: venice-la
role: founder
agent:
provider: anthropic
model: claude-3.5-sonnet
api_key_env: ANTHROPIC_API_KEY
max_tokens_per_session: 50000
max_cost_per_session_usd: 5.00
schedule: { mode: daily, time: "02:00", timezone: America/Los_Angeles }
focus_areas: [accessibility, environmental]
output: { directory: ~/.allocate/output/, auto_submit: false }
defaults:
neighborhood: venice-la8. Web Application
Page Structure (Next.js App Router)
(marketing)/ Landing, mission, docs, projects
(app)/
dashboard/ User's cross-neighborhood view
[neighborhood]/
page.tsx Neighborhood dashboard
projects/ Project list → detail → findings
findings/ Searchable finding list → detail
domains/[domain]/ Domain-specific view
volunteer/ Task board (Kanban) → detail + completion
proposals/ Proposal list → detail + voting
nonprofits/ Ecosystem map → detail
municipal/ Government dashboard, council, budget, officials
impact/ Impact dashboard with charts
members/ Member list + trust network graph → profiles
settings/ Neighborhood config (founder/admin)
contribute/ Contribution hub + web-based agent runner
settings/ User settingsKey Components
- FindingCard — domain badge, title, confidence indicator, verification status, staleness, source count
- TaskBoard — Kanban: Open → Claimed → In Progress → Completed → Verified
- VoteWidget — For/against/abstain, live tally via Supabase Realtime, discussion thread
- ImpactDashboard — Charts: tokens, hours, CO₂, findings merged, tasks completed, proposals passed
- NeighborhoodMap — Interactive Mapbox map with toggleable layers (findings, tasks, nonprofits)
- TrustNetwork — Force-directed graph showing vouch relationships
Realtime Features (Supabase Realtime)
- Live vote updates on proposals
- Live task claim/unclaim on task board
- New finding toast notifications
- Active agent session status
9. Session Artifact Materialization
The current POST /api/sync service route materializes terminal research
sessions into work-log and ledger artifacts. A service token is required. The
request may filter by neighborhood and terminal status, cap the batch size, or
run in dry-run mode; each session reports its own success or failure.
This release has no /api/sync/repo-to-db endpoint, bidirectional Git/Supabase
synchronization worker, or scheduled consistency workflow. Repository recovery
and hosted data operations are operator-controlled runbook procedures. Public
API behavior covers the current routes documented in API Routes.
10. Vouch Trust System Integration
Enhanced VOUCHED.td Format
# VOUCHED.td — Venice, LA
# Trust policy: see allocate.yaml
# Founding members
github:johnny_allocate founder 2026-02-08
github:maria_garcia vouched by johnny_allocate "neighbor, community garden organizer" 2026-02-10
# Community members
github:david_chen vouched by maria_garcia "completed 3 volunteer tasks, active in discussions" 2026-02-15
github:sarah_williams vouched by johnny_allocate "local journalist, covers Venice council" 2026-02-12
# Stewards (Tier 2)
+github:maria_garcia promoted by johnny_allocate "3 months active, 14 merged findings" 2026-05-10
# Nonconformities (confirmed; requires a cited standard and confirmation count)
-github:spam_account NC by david_chen "Agent Contract §2: fabricated sources in rf-2026-03-01-004" confirmed:3 2026-03-15
# Pending nonconformity (under review; effective status awaits confirmation)
~github:questionable NC by sarah_williams "Contribution Guidelines §4: possible plagiarism in rf-2026-04-02-001" confirmed:0 2026-04-02
# Trusted neighborhood lists (web of trust)
# @trust https://github.com/allocate/echo-park-la/blob/main/VOUCHED.td
# @trust https://github.com/allocate/mar-vista-la/blob/main/VOUCHED.tdParsing Rules (Updated)
- Lines starting with
+= steward promotion (Tier 2) - Lines starting with
-= confirmed nonconformity (effective, contributor blocked) - Lines starting with
~= pending nonconformity (under review; effective status awaits confirmation) - Lines starting with
# @trust <url>= inherit trust from another neighborhood - Dates at end of line = timestamp of action
- Quoted strings = reason
confirmed:N= number of confirmations on nonconformities- Comments (lines starting with
#without@trust) are ignored
Updated Database Schema
-- Replace the simple vouched_members table with a richer model
CREATE TABLE vouched_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
github_username TEXT NOT NULL,
-- Trust tier: 0 = unvouched, 1 = vouched, 2 = steward
trust_tier INTEGER NOT NULL DEFAULT 0 CHECK (trust_tier IN (0, 1, 2)),
-- Status tracks the current state
status TEXT NOT NULL CHECK (status IN ('pending', 'vouched', 'steward', 'nonconforming', 'nc_pending')),
-- Vouch provenance
vouched_by TEXT, -- github username of voucher
vouch_reason TEXT,
vouch_source TEXT DEFAULT 'direct', -- 'direct' or 'inherited:<neighborhood_slug>'
-- Steward promotion
promoted_by TEXT,
promoted_at TIMESTAMPTZ,
promotion_reason TEXT,
-- Nonconformity tracking
nc_issued_by TEXT,
nc_basis TEXT, -- must cite specific community standard
nc_confirmed_by TEXT[] DEFAULT '{}', -- list of confirming usernames
nc_confirmations_needed INTEGER DEFAULT 2,
nc_review_deadline TIMESTAMPTZ,
nc_contested BOOLEAN DEFAULT FALSE,
-- Activity tracking (for steward promotion eligibility)
first_contribution_at TIMESTAMPTZ,
merged_findings_count INTEGER DEFAULT 0,
completed_tasks_count INTEGER DEFAULT 0,
months_active INTEGER DEFAULT 0,
-- Metadata
commit_sha TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(neighborhood_id, github_username)
);
-- Vouch history (immutable audit log)
CREATE TABLE vouch_events (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
event_type TEXT NOT NULL CHECK (event_type IN (
'vouch', 'nc_initiated', 'nc_confirmed', 'nc_effective',
'nc_contested', 'nc_reversed', 'promote_steward', 'demote_steward'
)),
target_github TEXT NOT NULL, -- who is being acted upon
actor_github TEXT NOT NULL, -- who is taking the action
reason TEXT,
commit_sha TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Monthly vouch rate limiting
CREATE TABLE vouch_rate_limits (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
voucher_github TEXT NOT NULL,
month DATE NOT NULL, -- first of month
vouch_count INTEGER DEFAULT 0,
UNIQUE(neighborhood_id, voucher_github, month)
);
-- Indexes
CREATE INDEX idx_vouched_neighborhood ON vouched_members(neighborhood_id);
CREATE INDEX idx_vouched_status ON vouched_members(status);
CREATE INDEX idx_vouched_tier ON vouched_members(trust_tier);
CREATE INDEX idx_vouch_events_target ON vouch_events(target_github);
CREATE INDEX idx_vouch_events_actor ON vouch_events(actor_github);
CREATE INDEX idx_vouch_events_neighborhood ON vouch_events(neighborhood_id);
CREATE INDEX idx_vouch_rate_neighborhood ON vouch_rate_limits(neighborhood_id);Updated API Endpoints
# Vouch & Trust
GET /api/neighborhoods/:slug/members # List all members with trust tiers
GET /api/neighborhoods/:slug/members/:username # Member detail + vouch history
POST /api/neighborhoods/:slug/vouch # Vouch for user (rate limited)
POST /api/neighborhoods/:slug/nonconformity # Issue NC (requires standard citation)
POST /api/neighborhoods/:slug/nonconformity/:id/confirm # Confirm pending NC
POST /api/neighborhoods/:slug/nonconformity/:id/contest # Contest NC (corrective action request)
POST /api/neighborhoods/:slug/promote # Promote to steward (steward/founder only)
GET /api/neighborhoods/:slug/trust-network # Full trust graph data
GET /api/neighborhoods/:slug/trust-policy # Current trust configuration
# Tier 0 (unvouched) endpoints
POST /api/neighborhoods/:slug/staging # Submit unverified finding to staging
GET /api/neighborhoods/:slug/staging # Browse staged/unverified findings
POST /api/neighborhoods/:slug/tasks/:id/complete # Complete task (no vouch needed)Updated Web of Trust Resolution
resolveUserTrust(username, neighborhood, maxDepth=2):
1. Check local VOUCHED.td:
- If 'nonconforming' → return { status: 'nonconforming', tier: 0, source: 'direct' }
- If 'nc_pending' → return { status: 'active', tier: 1, source: 'direct', flag: 'nc_pending' }
- If 'steward' → return { status: 'active', tier: 2, source: 'direct' }
- If 'vouched' → return { status: 'active', tier: 1, source: 'direct' }
2. For each @trust directive (up to maxDepth):
a. Fetch trusted neighborhood's VOUCHED.td (with cycle detection via visited set)
b. If user found as 'vouched' or 'steward' in trusted list:
→ return { status: 'active', tier: 1, source: 'inherited:<slug>', maxTier: 1 }
(inherited trust has tier 1; steward status requires a direct record)
c. If user found as 'nonconforming':
→ check neighborhood's trust policy for nc_propagation
→ if 'none': skip (don't import nonconformities)
→ if 'opt_in': return { status: 'nc_inherited', tier: 0, source: 'inherited:<slug>' }
3. Not found → return { status: 'unknown', tier: 0 }Updated Authorization Middleware
requireTrust(request, neighborhoodSlug, minimumTier=1):
trust = resolveUserTrust(request.user.github, neighborhoodSlug)
if trust.tier < minimumTier:
if minimumTier == 0:
pass // Tier 0 always allowed
elif trust.status == 'nonconforming':
return 403 { error: 'NONCONFORMING', basis: trust.nc_basis, contest_url: '...' }
elif trust.status == 'unknown':
return 403 { error: 'NOT_VOUCHED', onramp: 'Complete volunteer tasks or open a discussion to get involved' }
elif trust.tier == 1 and minimumTier == 2:
return 403 { error: 'STEWARD_REQUIRED' }
request.trust = trust
next()Updated Permission Matrix
| Action | Min Tier | Notes |
|---|---|---|
| Browse findings, tasks, proposals | 0 | Public |
| Open issues and discussions | 0 | GitHub account required |
| Comment on proposals | 0 | During discussion period |
| Complete volunteer tasks | 0 | Photo/evidence required |
| Submit to staging (unverified) | 0 | Clearly marked unverified |
| Submit finding PR | 1 | Requires review to merge |
| Run agent with auto-submit | 1 | Rate limited |
| Vote on proposals | 1 | One vote per user |
| Vouch for new users | 1 | Rate limited (3/month default) |
| Issue nonconformity | 1 | Requires cited standard, starts review |
| Claim tasks | 1 | — |
| Approve/merge research PRs | 2 | Cannot merge own PRs |
| Confirm nonconformities | 2 | Independent review required |
| Modify neighborhood config | 2 | — |
| Add @trust directives | 2 | — |
| Promote to steward | Founder or steward consensus | — |
Cross-Neighborhood Nonconformity Registry
The runtime authority is the Supabase public.nc_registry table. Reviewed
/api/vouch/nc operations and their audit events are the supported mutation
path. A neighborhood may retain a VOUCHED.td file as a provenance artifact.
The Supabase table is the live authority for reviewed operations. This release
ships no separate registry repository or automatic file-to-database sync.
Registry Schema
-- Cross-neighborhood nonconformity registry
CREATE TABLE nc_registry (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
github_username TEXT NOT NULL,
neighborhood_id UUID REFERENCES neighborhoods(id) ON DELETE CASCADE,
neighborhood_slug TEXT NOT NULL,
reason TEXT NOT NULL,
nc_issued_by TEXT NOT NULL,
confirmed_by TEXT[] NOT NULL,
confirmed_at TIMESTAMPTZ NOT NULL,
contested BOOLEAN DEFAULT FALSE,
reversed BOOLEAN DEFAULT FALSE,
reversed_at TIMESTAMPTZ,
reversal_reason TEXT,
source_commit_sha TEXT, -- commit in neighborhood repo
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_registry_username ON nc_registry(github_username);
CREATE INDEX idx_registry_neighborhood ON nc_registry(neighborhood_slug);
CREATE INDEX idx_registry_active ON nc_registry(github_username) WHERE reversed = FALSE;Vouch and Nonconformity API
GET /api/vouch # List vouch records
POST /api/vouch # Create a vouch record
GET /api/vouch/:username # Read a contributor's vouch status
DELETE /api/vouch/:username # Remove a contributor's vouch record
POST /api/vouch/nc # Record a nonconformity
POST /api/vouch/nc/:id/confirm # Confirm a nonconformity
POST /api/vouch/nc/:id/contest # Contest a nonconformity
POST /api/vouch/nc/:id/reverse # Reverse a nonconformityNo /api/registry/* endpoint ships in this release.
Registry Sync Pipeline
The original blueprint proposed an automatic sync-on-merge workflow for
VOUCHED.td. The current repository has no such workflow. Registry changes
must use the reviewed application/database contracts and retain their
provenance. Automatic Git-to-database synchronization requires separate
implementation and release evidence.
Integration with Vouch Workflow
// Called before a vouch is committed
async function checkNonconformities(username: string): Promise<NCWarning[]> {
const records = await registry.getActive(username);
return records.map((r) => ({
neighborhood: r.neighborhood_slug,
reason: r.reason,
confirmed_at: r.confirmed_at,
nc_issued_by: r.nc_issued_by,
confirmed_by: r.confirmed_by,
}));
}
// In the vouch API handler
async function handleVouch(req: Request) {
const warnings = await checkNonconformities(req.body.target_username);
const policy = neighborhood.trust.cross_neighborhood_ncs.policy;
if (warnings.length > 0) {
if (policy === "hard_block") {
return 403({ error: "PRIOR_NONCONFORMITY", warnings });
}
if (policy === "soft_block" && req.user.trust_tier < 2) {
return 403({ error: "STEWARD_OVERRIDE_REQUIRED", warnings });
}
// advisory: log the override and proceed
await logVouchWithWarnings(req, warnings);
}
// Proceed with vouch...
}11. Impact Accounting Ledger
Design Philosophy
Raw resource inputs are immutable (recorded at time of action). Calculated impacts are recalculated whenever conversion factors improve. This means historical impact estimates get more accurate over time without changing the source data.
Conversion Factors (v1.0)
const CONVERSION_FACTORS_V1 = {
version: "1.0",
updated: "2026-02-08",
// Energy per token (kWh) — varies by model
energy_per_token: {
"claude-3.5-sonnet": 0.000000035, // ~35 nJ/token
"claude-3-opus": 0.00000009,
"gpt-4o": 0.00000004,
"gpt-4o-mini": 0.000000012,
"llama-3-70b": 0.00000006,
default: 0.00000004,
},
// Grid carbon intensity by region (kg CO₂/kWh)
grid_intensity: {
"us-west-2": 0.085, // Oregon (hydro)
"us-east-1": 0.379, // Virginia (mixed)
"us-west-1": 0.195, // California
default: 0.4,
},
// PUE by provider
pue: { anthropic: 1.1, openai: 1.1, google: 1.1, default: 1.2 },
// Human travel (kg CO₂/km)
travel_emissions: {
walked: 0,
biked: 0,
transit: 0.089,
drove: 0.217,
remote: 0,
},
// Network (kWh/GB)
network_energy_per_gb: 0.011,
};Calculation
For each ledger entry:
compute_kwh = total_tokens × energy_per_token[model] × pue[provider]
compute_co2 = compute_kwh × grid_intensity[region]
travel_co2 = travel_km × travel_emissions[method]
network_kwh = (total_tokens × 4 bytes / 1e9 GB) × network_energy_per_gb
total_co2 = compute_co2 + travel_co2 + (network_kwh × grid_intensity[region])Recalculation
POST /api/ledger is a service-token-only materialization/recalculation route
that can target one session or a bounded status/neighborhood set. No weekly
ledger workflow ships; invocation and any future scheduling require an
explicitly reviewed operator path.
Impact Aggregation SQL
CREATE FUNCTION get_neighborhood_impact(p_neighborhood_id UUID, p_start DATE, p_end DATE)
RETURNS JSON -- totals: tokens, seconds, cost, hours, travel_km, kwh, co2, finding_count, etc.12. CI/CD Pipeline
PR Validation (validate-pr.yml)
Triggered on pull-request changes. It classifies changed paths, checks supported changed-file formatting, requires evidence for protected public-contract changes, runs affected package type/tests, exercises repository scripts, and validates the launch-schema contract. It has read-only repository permissions and is not a deployment workflow.
Full Main Gate (full-gate.yml)
Triggered on pushes to main and manual dispatch. Independent jobs run the full
application lint/test/build gate, replay the isolated local database and pgTAP
suite, execute local production-build browser/accessibility/load checks, and run
pinned secret/static-analysis scanners. Passing this workflow supplies CI
evidence for one commit. Hosted-release evidence requires separate hosted
verification.
Web Delivery
There is no automatic web-deployment workflow. Railway delivery is a separate, explicitly authorized operation pinned to an approved release fingerprint, with hosted migration, DNS/TLS, connector, and write enablement retained as separate gates.
Publish Packages (publish-packages.yml)
Manual dispatch only. It requires a public repository and pre-reserved npm names, builds/tests exact SDK, agent, MCP, and CLI tarballs, smoke-tests the tarballs, generates a pinned SBOM/checksum manifest and attestations, then stages the packages. Staging never publishes them; final npm promotion remains a separate human-approved action.
13. Authentication & Authorization
Auth Flow
User clicks "Sign In"
→ Redirect to GitHub OAuth (Supabase Auth)
→ GitHub authenticates → callback to Supabase
→ Supabase issues JWT
→ Client sends JWT with all API requests
→ API validates JWT + checks vouch status per neighborhoodPermission Matrix (Three-Tier Model)
| Action | Min Tier | Auth | Notes |
|---|---|---|---|
| Browse findings, tasks, proposals | 0 | ✗ | Public |
| Open issues and discussions | 0 | ✓ | GitHub account required |
| Comment on proposals | 0 | ✓ | During discussion period |
| Complete volunteer tasks | 0 | ✓ | Photo/evidence required |
| Submit to staging (unverified) | 0 | ✓ | Clearly marked unverified |
| Submit finding PR | 1 | ✓ | Requires review to merge |
| Run agent with auto-submit | 1 | ✓ | Rate limited |
| Vote on proposals | 1 | ✓ | One vote per user |
| Vouch for new users | 1 | ✓ | Rate limited (3/month default) |
| Issue nonconformity | 1 | ✓ | Requires cited standard, starts review |
| Create proposal | 1 | ✓ | — |
| Create neighborhood | — | ✓ | Becomes founder + first steward |
| Approve/merge research PRs | 2 | ✓ | Cannot merge own PRs |
| Confirm nonconformities | 2 | ✓ | Independent review required |
| Modify neighborhood config | 2 | ✓ | — |
| Add @trust directives | 2 | ✓ | — |
| Promote to steward | Founder | ✓ | Or steward consensus |
Row Level Security
- Public read on: findings, tasks, proposals, neighborhoods, staging
- Tier 0 write on: staging findings, task completions, issues, discussion comments
- Tier 1 write on: findings (as PRs), tasks, proposals, votes, vouches
- Tier 2 write on: merge approvals, nonconformity confirmations, neighborhood config
- One-vote-per-user enforced at DB level via UNIQUE constraint
- Vouch rate limiting enforced at application level with DB tracking
- Nonconformity confirmation requires different user than issuer
14. Output Validation Engine
Pipeline
1. Parse YAML → error if malformed
2. Detect type field → error if unknown
3. Validate against Zod schema → field-level errors
4. Type-specific validation:
- Findings: source quality, resource inputs present, staleness defaults
- Tasks: location validity, expiration in future
- Work logs: step sequence integrity, total reconciliation
5. Privacy scan (PII detection via regex patterns)
6. Legal sensitivity auto-detection (named entity check)
7. Return { valid, errors[], warnings[] }PII Patterns Checked
- Email addresses
- Phone numbers (US format)
- Social Security Numbers
- Street addresses with house numbers
Validation Surface
Candidate artifacts use the shared SDK schema and authenticated validation contract. The legacy contributor CLI sits outside the public package surface. Local schema proof covers local behavior. Hosted write or publication readiness requires separate evidence.
15. Search & Discovery
Full-Text Search (PostgreSQL)
tsvectorcolumn on findings (title + summary + body + tags)plainto_tsqueryfor user queriests_rankfor relevance scoringpg_trgmindex on title for fuzzy matching
Spatial Search (PostGIS)
-- Find tasks near a location
SELECT id, title, urgency,
ST_Distance(location::geography, ST_MakePoint(lon, lat)::geography) / 1000 AS distance_km
FROM volunteer_tasks
WHERE status = 'open'
AND ST_DWithin(location::geography, ST_MakePoint(lon, lat)::geography, radius_m)
ORDER BY distance_km;Faceted Filtering
Findings: domain, confidence, verification status, staleness, project, contributor Tasks: status, urgency, difficulty, skills, location, timeframe
16. Notification System
Types
| Category | Events |
|---|---|
| Research | new_finding, finding_stale, pr_merged, pr_rejected |
| Volunteer | task_available (skill match), task_claimed, task_completed |
| Governance | proposal_open, proposal_closing_soon, proposal_result, vouched, nonconformity_issued |
| Agent | agent_session_complete, agent_error, agent_budget_warning |
Channels
- In-app (always) — stored in
notificationstable - Email (configurable per type) — via Supabase Edge Function + Resend
- Push (Phase 3) — via Expo notifications
17. Monitoring & Observability
Metrics
- Liveness: exact release identity and bounded database probe
- Protected-write readiness: OAuth/rate/control stores, reviewer, migration, capacity, and effective operating mode
- Application telemetry: privacy-allowlisted server/edge events with the exact release identifier
- External service metrics: Railway, Supabase, Redis, and Sentry evidence is environment-specific and must be verified after an authorized deployment
Health Check: GET /api/health
Returns only release-identity and database-liveness checks. GitHub, sync, ledger,
capacity, OAuth, rate-store, and protected-write readiness belong to
GET /api/ready.
Alerting
- Sentry for allowlisted application events
- Railway, Supabase, and Redis provider metrics after hosted verification
- GitHub Actions failure notifications for repository gates
- Required alert definitions are checked read-only; paging delivery still requires an accountable, separately configured recipient
18. Security Considerations
Threat Model
| Threat | Mitigation |
|---|---|
| Fabricated research | Validation + source verification + community review |
| Malicious vouch | Vouch rate limits + voucher accountability trail + nonconformity with confirmation |
| Bad actor neighborhood hopping | Cross-neighborhood nonconformity registry (advisory/soft_block/hard_block) |
| PII in outputs | Automated PII scanner + privacy review tier |
| API key exposure | Env vars only, CLI uses OS keychain |
| Webhook spoofing | GitHub signature verification |
| Vote manipulation | One vote per user (DB-enforced), vouch-gated |
| Data tampering in DB | RLS, service-token boundaries, stable identifiers, audit events, and operator-controlled backups |
| DDoS | Railway ingress controls plus bounded Upstash Redis rate policy; production load evidence is still required |
| Prompt injection | Validation sanitizes before web rendering |
| Agent runaway cost | Token budget + cost ceiling per session |
Rate Limiting
- Read endpoints: 100 requests/minute per IP
- Write endpoints: 20 requests/minute per authenticated user
- Webhook endpoint: bounded and signature-verified
- Contribution writes: shared mode/rate boundaries apply; public-write release is separately authorized
19. Performance Requirements
| Metric | Target |
|---|---|
| Page load (TTFB) | < 200ms |
| API response (read) | < 100ms |
| API response (write) | < 500ms |
| Full-text search | < 300ms |
| Spatial queries | < 200ms |
| Session artifact materialization (per session) | < 2s |
| Terminal-session materialization batch | < 5 min |
| Validation (per file) | < 1s |
| Concurrent users per neighborhood | 1000+ |
| Findings per neighborhood | 100k+ |
20. Deployment Architecture
Production Stack
Railway (Web App)
├── Next.js server and API routes
├── Static assets
└── Exact-fingerprint deployments after explicit authorization
Supabase (Database + Auth)
├── PostgreSQL 15 + PostGIS
├── Auth (GitHub OAuth)
├── Storage (evidence photos)
├── Edge Functions (async processing)
├── Realtime (live updates)
└── Pgbouncer (connection pooling)
GitHub (Source Control + CI)
├── allocate org
├── Neighborhood repos
├── Actions (CI/CD)
└── Webhooks → Railway-hosted web API
Upstash Redis (Rate Limiting)
Sentry (Error Tracking)
Railway service metrics (hosting)Environment Variables
The committed .env.example is the authoritative variable catalog. The
following matrix records the production boundary; values belong in the
deployment platform or an approved secret store, never in this document.
Required for production validation
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
# Release identity
ALLOCATE_RELEASE_SHA=
# Shared rate limiting
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
# Monitoring
SENTRY_DSN=
# Internal service credentials
SYNC_TOKEN=
CLI_JWT_SECRET=
ALLOCATE_AUTH_STATE_SECRET=
# Trusted production topology and operator control
ALLOCATE_TRUSTED_PROXY=railway
ALLOCATE_OPERATOR_TOKEN=
ALLOCATE_PLATFORM_REVIEWER_PROFILE_ID=
# Capacity policy (each metric is warning < scale-up < critical < write-disable)
ALLOCATE_CAPACITY_DATABASE_BYTES_WARNING=
ALLOCATE_CAPACITY_DATABASE_BYTES_SCALE_UP=
ALLOCATE_CAPACITY_DATABASE_BYTES_CRITICAL=
ALLOCATE_CAPACITY_DATABASE_BYTES_WRITE_DISABLE=
ALLOCATE_CAPACITY_MONTHLY_SPEND_CENTS_WARNING=
ALLOCATE_CAPACITY_MONTHLY_SPEND_CENTS_SCALE_UP=
ALLOCATE_CAPACITY_MONTHLY_SPEND_CENTS_CRITICAL=
ALLOCATE_CAPACITY_MONTHLY_SPEND_CENTS_WRITE_DISABLE=Optional or separately gated controls
# MCP transport and OAuth discovery
MCP_ISSUER=
MCP_RESOURCE_URL=
ALLOCATE_MCP_AUTH_REQUIRED=0
ALLOCATE_MCP_ALLOWED_HOSTS=
ALLOCATE_MCP_ALLOWED_ORIGINS=
ALLOCATE_MCP_ALLOW_LOCALHOST=0
ALLOCATE_OAUTH_CHATGPT_REDIRECT_URIS=
ALLOCATE_OAUTH_CLAUDE_REDIRECT_URIS=
ALLOCATE_OAUTH_EXTRA_REDIRECT_URIS=
MCP_DANGEROUSLY_ALLOW_INSECURE_ISSUER=0
MCP_ACCESS_TOKEN_TTL_SECONDS=
MCP_REFRESH_TOKEN_TTL_SECONDS=
MCP_AUTHORIZATION_CODE_TTL_SECONDS=
ALLOCATE_OAUTH_ALLOW_DCR=0
# Web safety and operations
ALLOCATE_OPERATING_MODE_OVERRIDE=
ALLOCATE_WRITE_RETRY_AFTER_SECONDS=300
ALLOCATE_API_BODY_BYTES_MAX=1048576
ALLOCATE_CAPACITY_STATE_MAX_AGE_SECONDS=3600
SITE_UNDER_CONSTRUCTION=
ALLOCATE_ENABLE_WRITABLE_BETA=
# Optional Supabase GitHub OAuth and rotation values
SUPABASE_AUTH_GITHUB_CLIENT_ID=
SUPABASE_AUTH_GITHUB_SECRET=
NEXT_PUBLIC_SUPABASE_BROWSER_URL=
CLI_JWT_SECRET_PREVIOUS=The MCP write flag opens only its separately gated OAuth route lane. Arbitrary
API writes remain outside that authority. DCR remains default-off, and the
legacy writable-beta flag is rejected by production validation. Local-only E2E,
package, load, and provider credentials are documented in .env.example for
local and CI use. Production configuration uses separately managed settings.
Agent and CLI process variables
# Provider credentials belong only in the agent process or approved secret store.
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
ALLOCATE_API_BASE_URL=
ALLOCATE_PROVIDER=
ALLOCATE_MODEL=
ALLOCATE_CONTRIBUTOR=
ALLOCATE_NEIGHBORHOOD=
ALLOCATE_API_KEY_ENV=
ALLOCATE_MAX_TOKENS=
ALLOCATE_MAX_COST_USD=
ALLOCATE_STATE_DIR=
ALLOCATE_MCP_TOKEN=Local and CI-only variables
ALLOCATE_E2E_HOST=
ALLOCATE_E2E_PORT=
ALLOCATE_E2E_LOCAL_RATE_LIMIT=
ALLOCATE_LOAD_BASE_URL=
ALLOCATE_LOAD_PORT=
ALLOCATE_LOAD_REQUESTS=1000
ALLOCATE_LOAD_SURFACE=all
ALLOCATE_PACKAGE_TARBALL_DIR=
ALLOCATE_TUI_PYTHON=
NEXT_RUNTIME=
NO_COLOR=These values select local test hosts, load-gate inputs, package artifacts, or runtime output behavior. They must not be used as hosted readiness or production-configuration evidence.
21. Testing Strategy
Test Pyramid
┌──────────┐
│ E2E │ Playwright: critical user flows
├──────────┤
┌───┤Integration├───┐ Supabase + transport mocks
│ ├──────────┤ │
│ │ Unit │ │ Zod schemas, validators, ledger calc
└───┴──────────┴───┘Unit Tests
- Schema validation: every Zod schema tested with valid/invalid fixtures
- PII scanner: known PII patterns detected, false positives checked
- Ledger calculator: known inputs → expected CO₂ outputs
- VOUCHED.td parser: edge cases (comments, nonconformities, trust directives)
- Web of trust resolver: cycle detection, max depth, multi-hop resolution
Integration Tests
- Disabled GitHub ingestion: fixed 410 without reading body/config or invoking fetch/database code
- API routes: authenticated/unauthenticated/vouched/nonconforming scenarios
- Vote counting: concurrent votes, duplicate prevention
- Task lifecycle: open → claim → complete → verify
E2E Tests (Playwright)
- Contributor flow: login → browse findings → view detail → verify
- Volunteer flow: login → browse tasks → claim → complete → upload evidence
- Governance flow: login → create proposal → vote → see results
- Public CLI flow:
allocate projects list→allocate evidence search→allocate releases show
Test Data
- Seed script (
supabase/seed.sql) with realistic test neighborhood - Fixtures directory with sample YAML outputs (valid and intentionally invalid)
- Mock GitHub API responses for webhook testing
22. Migration & Versioning Strategy
Database Migrations
Sequential SQL files in supabase/migrations/ are applied in numeric order.
The current launch-compatibility tail is:
00055_public_operations_limits_and_compatibility.sql
00056_atomic_finding_creation.sql
00057_sdk_metadata_limits_and_compatibility.sql
00058_finding_relationship_privacy.sql
00059_atomic_session_materialization.sqlThe first fifty-four migrations remain part of the ordered history. A local or
hosted target that stops before 00059 lacks the migration required by this
candidate's protected-write readiness contract.
New migrations via: npx supabase migration new <name>
Schema Versioning
All output types include a version field. Schema evolution rules:
- Additive changes (new optional fields): bump minor version, backward compatible
- Breaking changes (field removal, type change): bump major version, migration required
- Old versions validated with previous schema version during transition period
- Version routers and SDK validators handle accepted versions; migrations and operator procedures handle stored-data transitions
API Versioning
/api/v1is the current public read contract and preserves additive compatibility for existing fields and query aliases.- Breaking changes introduce an
/api/v2/prefix. - V1 maintained for 6 months after v2 launch
Package Versioning
- Semantic versioning for all npm packages
@allocate/sdkversion drives compatible agent/CLI versions- Lockstep releases for breaking changes
Appendix A: Neighborhood Data Repository Structure
Each neighborhood gets its own Git repo under allocate/:
venice-la/
├── README.md # Neighborhood overview
├── VOUCHED.td # Trust list
├── allocate.yaml # Neighborhood config
│
├── projects/
│ └── sunset-blvd-bike-lane/
│ ├── README.md # Project description
│ ├── research/
│ │ ├── rf-2026-02-08-001.yaml
│ │ └── rf-2026-02-08-002.yaml
│ ├── verification/
│ │ └── tc-2026-02-09-001.yaml
│ └── proposals/
│ └── cp-2026-02-10-001.yaml
│
├── monitoring/
│ ├── municipal/
│ │ ├── council-agendas/
│ │ └── budgets/
│ └── nonprofits/
│ ├── registry/
│ └── 990-filings/
│
├── tasks/
│ ├── open/
│ │ └── vt-2026-02-08-001.yaml
│ └── completed/
│ └── vt-2026-02-07-001.yaml
│
├── logs/
│ └── log-2026-02-08-001.yaml
│
└── .github/
└── workflows/
└── validate.yml # Inherits from main repoAppendix B: Example Agent System Prompt
You are an Allocate research agent working for the {neighborhood} community.
Your job is to research {domain} issues using public data sources and produce
structured findings that help residents understand their neighborhood.
## Rules (Agent Contract)
1. Flag uncertainty. Include confidence with justification.
2. Every claim must link to a verifiable primary source.
3. Record all resource inputs (tokens, API calls, cost, model, provider).
4. Never identify individuals. Aggregate vulnerable populations.
5. Stay in scope: only {neighborhood} / {project}.
6. Document every step, including what failed.
7. Separate factual findings from recommendations.
8. Fail gracefully. Log errors, produce partial findings, never fabricate.
## Dual-Output Requirement
Every finding MUST include BOTH:
### 1. Plain-Language Summary (REQUIRED)
Write for a resident with no technical or regulatory background.
- `tldr`: One sentence, max 280 characters. What's the problem in plain English?
- `situation`: What did you find? Be specific about locations and conditions.
- `impact`: Why should residents care? Who is affected?
- `recommendation`: What could be done about it?
Example:
tldr: "The curb ramp at Venice Blvd and Pacific Ave is missing tactile warning strips, making it dangerous for visually impaired pedestrians."
situation: "The curb ramp at the northwest corner of Venice Blvd and Pacific Ave was repaved. The detectable warning surface (truncated domes) was not reinstalled. The ramp surface is smooth concrete with no tactile or color contrast differentiation from the roadway."
impact: "Visually impaired pedestrians cannot detect the transition from sidewalk to street, creating a serious safety hazard at a high-traffic intersection near the Venice library and senior center."
recommendation: "File a complaint with LA Bureau of Street Services requesting installation of ADA-compliant detectable warning surfaces. Reference ADA Standard 406.13."
### 2. Conformity Assessment (STRONGLY ENCOURAGED)
Map your findings to formal standards whenever possible. This makes findings
usable in government filings, grant applications, public comment submissions,
and legal complaints. Always attempt to identify applicable standards. Research
the applicable standard when its scope is unclear.
For each applicable standard clause, assess:
- `status`: conforming / nonconforming / partially_conforming / not_assessed
- `severity`: observation (informational) / minor / major / critical
- `evidence`: Specific evidence supporting your assessment
- `corrective_action`: What would bring this into conformity
Common standards by domain:
- Accessibility: ADA 2010, CA Building Code 11B, MUTCD Ch 4E
- Environmental: CEQA, Clean Water Act, Clean Air Act, local ordinances
- Housing: Fair Housing Act, local rent stabilization, habitability standards
- Infrastructure: MUTCD, AASHTO, local complete streets policies
- Safety: OSHA, fire code, building code
If no formal standard applies, record that in `scope_note` and provide the
plain-language summary. A finding may use a community-defined standard from a
passed proposal when no formal regulation applies.
## Output Format
Produce a YAML file matching the ResearchFinding schema (v1.0).
Produce a separate YAML work log matching the AgentWorkLog schema (v1.0).
## Current Task
{task_description}
## Available Standards
{available_standards}
## Available Tools
{tool_descriptions}Appendix C: Quick Start (Developer)
# Clone (private maintainer/invited-collaborator access)
git clone <maintainer-provided-private-origin>
cd allocate
# Install
corepack enable
pnpm install --frozen-lockfile
# Environment
cp .env.example apps/web/.env.local
# Keep real secrets out of Git; fill only the required local values.
# Named local database + deterministic local account/bootstrap
pnpm db:start
pnpm env:check
# Dev
pnpm dev
# CLI (separate terminal, only after separately authorized npm publication)
npm install --global @allocate/cli
# Default launch surface is public/read-only and needs no provider key:
allocate projects list --json
allocate resources list --json
# `allocate mcp stdio` exposes the same anonymous public catalog over stdio.Allocate Technical Specification v0.1.0 — status reviewed August 14, 2026 MIT License; current source status: docs/PROJECT_STATUS.md