{"slug":"PoppyPetitions","total":6,"limit":100,"offset":0,"since":null,"commits":[{"hash":"77c4ca8","date":"2026-05-31 13:44:42 -0700","author":"Steve Abrams","subject":"PoppyPetitions P1: dedicated audit_events table + lib/audit.ts best-effort logger","body":"Fills the gap from P1-E (b444995), which stored the session<->agent binding\nin compute_usage.metadata - that only has rows when Gemini bills compute, so\nnon-billing audit-worthy actions (denied agent lookups, rejected oversized\nwrites, logins) left no trail. New poppy.audit_events table + logAudit() cover\nall AI/write routes, best-effort (never throws, never blocks the request).\n\n- migrations/0001_audit_events.sql: idempotent, additive-only, no down-migration.\n  Apply manually to the poppy DB. NOT to be applied to the compromised prod\n  Kamatera host until it is rebuilt/cleaned.\n- lib/audit.ts: logAudit / requestProvenance / auditFromRequest.\n- Full-project tsc --noEmit: zero errors.\n- Wiring logAudit into the petitions/vote/comment POST routes is a separate\n  follow-up, beyond this board item's scope.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"438eef6","date":"2026-05-30 12:32:22 -0700","author":"Steve Abrams","subject":"P1-F: compute_usage NULL race — safeNumber + petition_id backfill","body":"Two NULL-race / data-integrity issues in compute_usage:\n\n1. petitions POST always wrote compute_usage.petition_id=NULL even on\n   success — INSERT order was compute_usage → petition, so the FK wasn't\n   known when the audit row landed. Fix: capture compute_usage.id via\n   RETURNING, then UPDATE petition_id after the petition INSERT succeeds.\n   Best-effort backfill — if the UPDATE fails the audit row stays unlinked\n   (same as today), but on the happy path analytics can now join the two.\n\n2. All four routes (petitions/vote/comment/seed) read geminiResult.{input,\n   output,total}Tokens raw from lib/gemini.ts which uses ?? 0 — catches\n   null/undefined but NOT NaN. A malformed Gemini response can leak NaN\n   into pg INSERTs (int/bigint columns throw, numeric throws, double\n   precision accepts silently). Fix: safeNumber() coerces NaN/Infinity to\n   0 at the boundary before every compute_usage / agents UPDATE.\n\nAlso rolled the existing buildAuditMetadata() into agents/seed/route.ts\n(the P1-E change covered the three write routes but missed seed).\n\nAll five files esbuild-parse clean."},{"hash":"b444995","date":"2026-05-30 12:27:59 -0700","author":"Steve Abrams","subject":"P1-E: agent_id session-binding via validate-and-audit (DTD verdict B)","body":"Add lib/agentGuard.ts (resolveActingAgent + buildAuditMetadata) and wire into\nthe three write routes (petitions POST, vote POST, comment POST). Closes two\ngaps in the previous existence-only check:\n\n1. resolveActingAgent now requires is_active=true. Returns 404 not-found,\n   409 inactive, 500 db-error — distinct codes so callers can branch.\n\n2. compute_usage metadata JSONB now carries acting_session (the verifyAuth\n   user) + ts. Forensic queries can reconstruct the session ↔ agent ↔ action\n   triple from one row.\n\nDTD picked B over A (true session-binding via cookie) and C (mixed default+\noverride): single-admin loopback model doesn't justify the UX refactor A\nrequires; the threat being closed is the agent existence/active gap + the\nabsence of forensic traceability, and B handles both without schema change.\n\nNo UI changes. No schema migration (metadata is JSONB). All four files\nesbuild-parse clean."},{"hash":"f771a91","date":"2026-05-30 10:00:36 -0700","author":"Steve","subject":"docs: redact secret literal from db.ts scrub-comment (gitleaks hygiene)","body":""},{"hash":"7f44f02","date":"2026-05-30 08:37:23 -0700","author":"Steve","subject":"P1-B: defensive input-length caps on petition write endpoints","body":"Add lib/validate.ts (clampLen / firstLengthViolation + LIMITS) and reject\noversized free-text inputs with HTTP 400 before any DB write or Gemini call,\nhardening against prompt-injection / abuse payloads.\n\nCaps: title 200, summary/comment/reason 2000, body 5000, identifiers 200.\n- petitions POST: title, summary, body, author_id\n- vote POST: agent_id, petition id (rationale is AI-generated)\n- comment POST: comment_body, agent_id, parent_id, petition id\n\nGuards only reject present oversized strings; absent/non-string values pass\nthrough so ai_generate paths and existing required-field errors are unchanged.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>"},{"hash":"f3826a6","date":"2026-05-06 10:22:14 -0700","author":"Steve Abrams","subject":"initial scaffold (2026-05-06 overnight session)","body":""}]}