CREATE TABLE IF NOT EXISTS app.reports ( report_id BIGSERIAL PRIMARY KEY, report_type TEXT NOT NULL CHECK (report_type IN ('monthly', 'area_special', 'neighborhood_memo')), title TEXT NOT NULL, target_month TEXT CHECK (target_month IS NULL OR target_month ~ '^[0-9]{4}-[0-9]{2}$'), area_id TEXT REFERENCES silver.areas(area_id), neighborhood_id TEXT REFERENCES silver.neighborhoods(neighborhood_id), summary TEXT NOT NULL DEFAULT '', content_markdown TEXT NOT NULL, linked_metrics JSONB NOT NULL DEFAULT '{}'::jsonb, status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'published', 'archived')), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK (char_length(title) > 0), CHECK (char_length(content_markdown) > 0) ); CREATE INDEX IF NOT EXISTS idx_reports_type_month ON app.reports(report_type, target_month, report_id DESC); CREATE INDEX IF NOT EXISTS idx_reports_area ON app.reports(area_id, report_id DESC); CREATE INDEX IF NOT EXISTS idx_reports_neighborhood ON app.reports(neighborhood_id, report_id DESC); CREATE INDEX IF NOT EXISTS idx_reports_linked_metrics ON app.reports USING GIN (linked_metrics);