← back to Codex Review 2026 04 30

CONSENSUS.md

1148 lines

# All-night CONSENSUS — 2026-04-30
Generated 2026-04-30T23:48:03-07:00

Round-3 attempted but failed (OpenAI quota — see _QUOTA_HALT.md). Per-project synthesis below uses rounds 1, 2, and the codex re-review.

# Consensus — AgentAbrams
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `snippets/redact_lint.py:71-73`, `.github/workflows/ci.yml:34-40`: detected secrets are printed with surrounding snippet into CI logs. Fix by printing only pattern name + file/line, never matched content.
- `video/oauth_authorize.py:31-32`: refresh token is printed to stdout, making terminal logs/screen recordings a secret leak. Fix by writing only to a chmod-600 local file or clipboard prompt, not stdout.
- `video/upload_youtube.py:38`, `.github/workflows/youtube.yml:36-44`: uploads default to `privacy="public"` with no workflow privacy input; wrong artifact/path becomes public immediately. Default to `private`/`unlisted` and require explicit public promotion.


## Round-1 applied
## Applied
- None — no P0/P1 issue in `CODEX_REVIEW.md` matched the autonomous safe-fix categories 1-7.


## Still deferred for Steve
- P0 — `snippets/redact_lint.py:71`, `.github/workflows/ci.yml:34` — redaction lint can print matched secret snippets into CI logs — deferred because log redaction behavior is not one of the approved autonomous safe fixes.
- P0 — `video/oauth_authorize.py:31` — OAuth refresh token is printed to stdout after local authorization — deferred because changing token handling/storage is outside the approved safe list and may affect Steve's credential workflow.
- P0 — `video/upload_youtube.py:38`, `.github/workflows/youtube.yml:36` — YouTube uploads default to public with no workflow privacy input — deferred because release/privacy workflow changes are not on the approved safe list.
- P1 — `media/engine.py:67`, `media/engine.py:86`, `media/engine.py:103` — media generation errors are swallowed while the build reports complete — deferred because build failure policy changes are outside the approved safe list.
- P1 — `media/engine.py:75` — chapter data is generated but not written to `media/output/chapters.json` — deferred because this is a behavior/output change outside the approved safe list.
- P1 — `media/engine.py:28` — output path is cwd-relative and can write to the wrong directory — deferred because this is not a hardcoded absolute external path covered by safe fix 3.
- P1 — `README.md:47`, `media/md_to_blog.py:99` — docs reference missing dependency manifest/requirements flow — deferred because dependency manifest work is explicitly not safe to generate without testing.
- P1 — `podcast/rss_generator.py:42` — RSS items omit enclosure length, GUID, and dynamic escaped description content — deferred because feed contract changes are outside the approved safe list.
- P1 — `media/md_to_podcast.py:31` — podcast text truncates mid-word or mid-section — deferred because narration policy changes are outside the approved safe list.
- P1 — `media/social.py:21` — X post claims a 280-character limit but does not enforce it — deferred because social-copy behavior changes are outside the approved safe list.
- P1 — `snippets/precommit.sh:30` — `.env` protection only checks staged files and can pass without git metadata — deferred because pre-commit policy changes are outside the approved safe list.


---

# Consensus — bankrupt-leads
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- [src/poller.py](/Users/stevestudio2/Projects/bankrupt-leads/src/poller.py:37): only requests first 100 dockets and never follows pagination; busy courts silently lose leads. Fix: loop CourtListener `next` pages until exhausted.
- [src/poller.py](/Users/stevestudio2/Projects/bankrupt-leads/src/poller.py:45): poller inserts cases only; no code populates `filings`, downloads PDFs, or extracts text, so classify/extract/digest have no real input. Fix: implement filing/document ingestion or mark current scripts as nonfunctional scaffold.
- [src/extract.py](/Users/stevestudio2/Projects/bankrupt-leads/src/extract.py:77): extraction is not idempotent; rerunning a filing duplicates assets every time. Fix: add a natural key/unique constraint or delete/upsert assets per `filing_id` in one transaction.
- Secrets/auth/SQL injection: none observed.


## Round-1 applied
## Applied

- None — CODEX_REVIEW.md did not list P0/P1 findings matching the approved safe-fix categories.


## Still deferred for Steve

- P0 — src/poller.py:37 — poller only requests the first 100 CourtListener dockets and does not follow pagination — deferred because pagination changes pipeline behavior and is not one of the approved autonomous safe fixes.
- P0 — src/poller.py:45 — poller inserts cases only; no filing/document ingestion, PDF download, or text extraction path populates downstream inputs — deferred because implementing ingestion is a product/workflow change, not a safe surgical fix.
- P0 — src/extract.py:77 — extraction is not idempotent and reruns can duplicate assets — deferred because the reviewed fix requires schema/uniqueness or transactional delete/upsert semantics, and database schema/migration work is explicitly out of scope.
- P1 — src/llm.py:34 — malformed or empty Ollama JSON output can crash the job without filing-level error state — deferred because durable retry/error-state handling changes workflow semantics and was not in the approved safe-fix list.
- P1 — src/classify.py:70 — one missing or unreadable text_path aborts the whole classification batch after prior commits — deferred because per-filing error handling/state semantics were not in the approved safe-fix list.
- P1 — src/extract.py:91 — arbitrary LLM asset keys and invalid dates/numerics can fail DB writes mid-run — deferred because validation/coercion policy is domain-specific and not one of the approved safe fixes.
- P1 — src/poller.py:76 — one court request failure skips all remaining courts — deferred because court-level retry/logging behavior was not in the approved safe-fix list.
- P1 — src/schema.sql:27 — filings.case_id allows null and lacks ON DELETE behavior — deferred because SQL/schema changes are explicitly prohibited.


---

# Consensus — bubbesblock
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env:2`, `.env:3`, `.env:5`: real API keys live in the project root; accidental sync/commit leaks Google/DPLA access. Rotate keys if shared, keep `.env` out of VCS/backups, and enforce domain/IP/API restrictions.
- `README.md:9`: claimed-address posting gate is only documented, with no enforceable auth code present. If this repo is deployed as-is or rebuilt later, posting can ship without verification; implement server-side verification against `claimmyaddress.com`.


## Round-1 applied
## Applied
- None — no P0/P1 review item matched the approved autonomous safe-fix list in the current project contents.


## Still deferred for Steve
- P0 — `.env:2` — real `GOOGLE_MAPS_API_KEY` appears to live in the project root environment file — deferred because `.env` must be left alone and any real key exposure requires Steve to rotate/restrict the key.
- P0 — `.env:3` — real `GOOGLE_GEOCODING_API_KEY` appears to live in the project root environment file — deferred because `.env` must be left alone and any real key exposure requires Steve to rotate/restrict the key.
- P0 — `.env:5` — real `DPLA_API_KEY` appears to live in the project root environment file — deferred because `.env` must be left alone and any real key exposure requires Steve to rotate/restrict the key.
- P0 — `README.md:9` — posting verification is documented but no enforceable server-side auth code exists in this repo — deferred because implementing identity/claim enforcement is product/application work, not one of the approved safe fixes.
- P1 — `README.md:5` — master plan points to a sibling-relative file, so the repo is not self-contained — deferred because copying or defining the operational contract is documentation/product scope outside the approved safe-fix list.
- P1 — `README.md:7` — cross-domain identity/history flow lacks API contracts, trust model, and outage behavior — deferred because designing those contracts is architecture work outside the approved safe-fix list.
- P1 — project root — no source, tests, package/deploy config, or datastore schema is present — deferred because creating application scaffolding or tests would exceed the requested surgical safe fixes.


---

# Consensus — claimmyaddress
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env:2-5`: live API keys are stored in plaintext in the project directory; if synced, backed up, or later committed, they are exposed. Rotate these keys, move secrets to a real secret store, and keep only `.env.example`.
- `.env:3`: `GOOGLE_GEOCODING_API_KEY` is a server-side key; if not strictly IP/service-account restricted, it can be abused for billable requests. Verify restrictions in GCP and rotate after removal.
- `README.md:7-9`: this repo claims it handles signup, OAuth, document upload, inventory, and insurance export, but no implementation exists here. Security review of auth/upload/data handling cannot be performed from this repo; review the shared backend before launch.


## Round-1 applied
## Applied
- CHANGES.md:1 - documented the safe-fix pass - required by FIX mode; no source-code safe fixes were available in this scaffold-only repo.


## Codex re-review verdict: SHIP IT

## New issues introduced
none observed


## Still deferred for Steve
- P0 - .env:2-5 - live Google Maps, Google Geocoding, and DPLA API keys are stored in plaintext - deferred because rotating real secrets and moving them to a secret store requires Steve; `.env` was left untouched.
- P0 - .env:3 - `GOOGLE_GEOCODING_API_KEY` may be billable if not strictly restricted - deferred because verifying GCP restrictions and rotating the key requires Steve.
- P0 - README.md:7-9 - repo claims signup, OAuth, claims, uploads, inventory, insurance export, shared backend, DB, and MCP, but no implementation exists here to review - deferred because the shared backend location and production architecture need Steve.
- P1 - README.md:5 - master plan lives outside this repo - deferred because copying architecture/runbook details is documentation work outside the approved safe-fix list.
- P1 - .env.example:1-2 - `DPLA_API_KEY` placeholder is missing - deferred because this was not a hardcoded source fallback secret and is outside the approved safe-fix list for this pass.
- P1 - Project root - `.gitignore` is missing while `.env` exists beside project files - deferred because adding repo policy files was outside the approved safe-fix list for this pass.
- P1 - Project root - no package manifest, tests, routes, DB migrations, or deploy config exist - deferred because creating application structure is not a small surgical safe fix.


---

# Consensus — dear-bubbe-next
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- none observed.


## Round-1 applied
## Applied

- None — no P0 issues were observed in `CODEX_REVIEW.md`, and no P1 issue matched the approved safe fix categories.


## Still deferred for Steve

- P1 — `.gitignore:1` / `CODEX_REVIEW.md:11` — project source/config files are absent, so the project cannot be built, run, or meaningfully reviewed — deferred because restoring source or choosing the correct project directory is outside the approved safe fixes.
- P1 — `.gitignore:8` / `CODEX_REVIEW.md:12` — `.env` files are ignored but no `.env.example` or config docs exist — deferred because there is no source/config to derive sanitized required variables from, and inventing env vars would violate the surgical-fix constraint.
- P1 — repository root / `CODEX_REVIEW.md:13` — no `.git` directory is present, so history/status cannot be inspected — deferred because restoring Git metadata or relocating the review target needs Steve.


---

# Consensus — Designer-Wallcoverings
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `DW-Agents/ecosystem.config.js:45`, `:72`, `:92`, `:134`, `:176`, `:494`; `brand-router/server.js:11`; `DW-Websites/shop-by-color/server.js:9`: real-looking Shopify/Slack/Anthropic/Zendesk/Postgres secrets are hardcoded. Rotate every exposed credential, purge from files/history/backups, load only from env/secret store.
- `.env:1`, `shopify/.env:1`, `DW-MCP/.env:1`, `DW-Agents/.env:1`: live secret-bearing env files sit inside the project tree. Confirm whether they are tracked/backed up; rotate and move to an untracked secrets location.
- `DW-Agents/dw-agents/agent-legal-team/legal-agent.ts:248-284`, `:1894`, `:2151`: auth middleware is not applied, while unauthenticated routes can run Claude analysis and set Shopify products to draft. Add global auth before routes and separately verify Shopify webhook HMAC.
- `admin/server.ts:19`, `:30`, `:61`, `:421-424`: admin interface falls back to `admin123`, uses `Math.random()` bearer tokens, allows `Access-Control-Allow-Origin: *`, and prints the admin password. Require `ADMIN_PASSWORD`, use `crypto.randomBytes`, restrict CORS, never log secrets.
- `DW-MCP/src/index.js:300-345`: Shopify OAuth callback has no state validation and logs/displays the new access token. Add OAuth `state` validation, authenticate token-generation flow, and never render/log tokens.
- `DW-Agents/dw-agents/agent-slack/slack-agent.ts:275-280`: Slack events endpoint accepts unsigned JSON; `SLACK_SIGNING_SECRET` is defined but unused. Verify Slack request signatures before calling Claude or posting replies.
- `DW-MCP/src/index.js:369-375`: Shopify order webhook reads HMAC but does not verify it. Reject unsigned/invalid webhooks before posting customer/order data to Slack.


## Round-1 applied
## Applied
- admin/server.ts:19 — replaced `ADMIN_PASSWORD` fallback with required env lookup and fail-fast error — removes hardcoded default admin credential.
- admin/server.ts:427 — stopped printing the configured admin password at startup — prevents runtime secret disclosure in logs.
- brand-router/server.js:11 — replaced hardcoded Shopify token with `BRAND_ROUTER_SHOPIFY_TOKEN` and fail-fast check — removes exposed token from source.
- brand-router/server.js:13 — replaced hardcoded Gemini key with `BRAND_ROUTER_GEMINI_API_KEY` and fail-fast check — removes exposed API key from source.
- brand-router/server.js:14 — replaced hardcoded basic-auth password with `BRAND_ROUTER_PASSWORD` and fail-fast check — removes exposed admin password from source.
- DW-Websites/shop-by-color/server.js:9 — replaced hardcoded Shopify token with `SHOP_BY_COLOR_SHOPIFY_TOKEN` and fail-fast check — removes exposed token from source.
- DW-Agents/dw-agents/in-parallel-agent.ts:611 — moved `message` extraction before use and added a 400 guard for missing/invalid body — prevents a request-body crash.
- .env.example:12 — added placeholder env vars for the updated admin, brand-router, and shop-by-color settings — documents required secrets without storing real values.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
brand-router/create-vendor-collections.js:479 — `/api/regenerate-html` call still authenticates as `admin:DWSecure2024!`; unless `BRAND_ROUTER_PASSWORD` is set to that exact old password, the workflow now gets 401 and HTML regeneration silently degrades to a warning — severity P1


## Round-2 applied
## Round 2 Applied
- brand-router/create-vendor-collections.js:24 — replaced the remaining hardcoded Shopify token with `BRAND_ROUTER_SHOPIFY_TOKEN` and fail-fast validation — fixes the re-review missed call-site at line 24 and the `brand-router/server.js` token patch marked `concern`.
- brand-router/create-vendor-collections.js:29 — added `BRAND_ROUTER_PASSWORD` lookup with fail-fast validation — supports the same auth env var as `brand-router/server.js`, fixing the re-review `STATUS broken` companion-client password finding.
- brand-router/create-vendor-collections.js:487 — replaced the hardcoded `admin:DWSecure2024!` regenerate auth with `admin:${BRAND_ROUTER_PASSWORD}` — fixes the re-review missed call-site and P1 new issue where `/api/regenerate-html` could return 401 after the server password moved to env.
- brand-router/create-vendor-collections.js:490 — made 401 regeneration responses fatal before the Shopify page push — addresses the re-review recommendation to avoid silently pushing stale HTML when auth fails.


## Still deferred for Steve
- P0 — DW-Agents/ecosystem.config.js:45 — hardcoded production-looking secrets — PM2 ecosystem files are explicitly out of scope; rotate real credentials.
- P0 — .env:1 — live secret-bearing env files in project tree — `.env` files were left untouched; confirm tracking/backups and rotate externally.
- P0 — shopify/.env:1 — live secret-bearing env file — `shopify/` is explicitly out of scope; confirm tracking/backups and rotate externally.
- P0 — DW-MCP/.env:1 — live secret-bearing env file — `.env` files were left untouched; confirm tracking/backups and rotate externally.
- P0 — DW-Agents/.env:1 — live secret-bearing env file — `.env` files were left untouched; confirm tracking/backups and rotate externally.
- P0 — DW-Agents/dw-agents/agent-legal-team/legal-agent.ts:248 — missing auth before destructive/Claude routes — auth design and Shopify HMAC validation are beyond the safe list.
- P0 — admin/server.ts:30 — wildcard CORS and weak token generation remain — not included in the approved safe-fix list.
- P0 — DW-MCP/src/index.js:300 — OAuth callback lacks state validation and exposes generated token — auth flow changes are beyond the safe list.
- P0 — DW-Agents/dw-agents/agent-slack/slack-agent.ts:275 — Slack events are not signature-verified — webhook auth change is beyond the safe list.
- P0 — DW-MCP/src/index.js:369 — Shopify order webhook HMAC is not verified — webhook auth change is beyond the safe list.
- P1 — admin/server.ts:362 — failed-products query ignores `sessionId` — database behavior change was deferred because it is outside the safe list.
- P1 — brand-router/server.js:158 — GraphQL query interpolates user input — not one of the approved safe fixes.
- P1 — DW-MCP/monitor-new-orders.js:14 — polling/state ordering can skip or duplicate notifications — requires behavior redesign, not a surgical safe fix.
- P1 — brand-router/server.js:35 — synchronous brand-data read/write can block or race — datastore/concurrency change was deferred.


---

# Consensus — Forza
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `index.html:17-21` public root is a “3 theme mockups” chooser, not the winning production site; visitors land on internal design artifacts. Fix: make root serve `theme-3-costa-rica-modern/` or redirect permanently.
- `theme-3-costa-rica-modern/contact/index.html:115`, `theme-3-costa-rica-modern/index.html:555` lead forms call `preventDefault()` and never send data; real leads are silently lost or shown a mockup alert. Fix: wire to a form backend/mail handler with success/error states.
- `theme-3-costa-rica-modern/index.html:14`, `contact/index.html:13`, `about/index.html:15`, `services-legal/index.html:15` load Figma MCP capture JS in production pages; third-party design tooling can execute on every visitor session. Fix: remove from deploy or gate behind local/dev-only build.
- `README.md:6`, `README.md:63`, `DIARY.md:164` SSL/DNS are documented as unfinished; site may be unreachable or HTTP-only. Fix: confirm DNS A record, run certbot, force HTTPS.
- Known SQL injection/auth/CVE issues: none observed; there is no backend, auth layer, SQL, or package manifest.


## Round-1 applied
## Applied

- None — no P0/P1 review item matched the approved safe fix list for autonomous edits.


## Still deferred for Steve

- P0 — `index.html:17` — public root serves the theme mockup chooser — deferred because changing production routing/customer-facing HTML is out of scope.
- P0 — `theme-3-costa-rica-modern/contact/index.html:115`, `theme-3-costa-rica-modern/index.html:555` — lead forms prevent submission and do not send data — deferred because wiring a real lead backend/mail handler requires a product/infrastructure decision.
- P0 — `theme-3-costa-rica-modern/index.html:14`, `theme-3-costa-rica-modern/contact/index.html:13`, `theme-3-costa-rica-modern/about/index.html:15`, `theme-3-costa-rica-modern/services-legal/index.html:15` — production pages load Figma MCP capture JavaScript — deferred because removing scripts from public-facing pages is outside the approved safe fix list and may affect the current design workflow.
- P0 — `README.md:6`, `README.md:63`, `DIARY.md:164` — SSL/DNS are documented as unfinished — deferred because DNS, certbot, nginx, and certificate work are explicitly excluded and require Steve/server access.
- P0 — `CODEX_REVIEW.md:13` — no SQL injection/auth/CVE backend issue observed — deferred because there is no fix to apply.
- P1 — `README.md:27`, `DIARY.md:116`, `DIARY.md:272` — public docs include root SSH/deploy runbook details — deferred because this is documentation/content cleanup, not one of the approved safe fixes.
- P1 — `theme-3-costa-rica-modern/contact/index.html:184` — privacy policy link is `href="#"` while collecting personal data — deferred because adding a real privacy policy is a legal/content decision.
- P1 — `theme-3-costa-rica-modern/index.html:489`, `theme-3-costa-rica-modern/index.html:551`, `theme-3-costa-rica-modern/contact/index.html:249`, `theme-3-costa-rica-modern/contact/index.html:321` — core CTAs are dead `#` links — deferred because replacing them changes public-facing behavior/content and needs real destination choices.
- P1 — `theme-3-costa-rica-modern/index.html:15` — homepage duplicates shared CSS — deferred because CSS migration is not in the approved safe fix list and touches customer-facing presentation.
- P1 — `README.md:41` — docs say About and Legal are planned though files exist — deferred because documentation cleanup is outside the approved safe fix list.


---

# Consensus — Hormuzy
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `clone/index.html:43-46`: references `/assets/index-DNA8aLxR.js`, `/assets/three-s7MRw7i5.js`, `/assets/r3f-DrPHa5mf.js`, `/assets/index-Bjz6pQij.css`, none present in repo; local/static deploy renders empty `#root`. Fix by committing full build output or restoring source/build pipeline.
- `clone/manifest.json:17`: references `/icon-512.png`, not present; PWA install/icon fetch breaks. Add the asset or remove the manifest entry.
- Secrets/auth/SQL injection/prod credentials: none observed.


## Round-1 applied
## Applied
- `clone/recon.js:1` — replaced the hardcoded absolute Playwright dependency path with `require('playwright')` — removes a machine-specific `/root/Projects/Designer-Wallcoverings/...` reference without touching the excluded path.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`clone/recon.js:1` — script now requires an undeclared dependency; `node` fails before any recon logic runs with `MODULE_NOT_FOUND: Cannot find module 'playwright'` — P1


## Round-2 applied
## Round 2 Applied
- None.


## Still deferred for Steve
- P0 — `clone/index.html:43` — missing referenced build assets (`/assets/index-DNA8aLxR.js`, `/assets/three-s7MRw7i5.js`, `/assets/r3f-DrPHa5mf.js`, `/assets/index-Bjz6pQij.css`) — deferred because restoring/committing build output is outside the safe-fix list and may require the original build pipeline.
- P0 — `clone/manifest.json:17` — references missing `/icon-512.png` — deferred because adding/removing public PWA assets is outside the safe-fix list and changes deploy-visible behavior.
- P1 — `clone/index.html:42`, `clone/index.html:50` — third-party scripts lack CSP/SRI containment — deferred because public-facing HTML/security policy changes are outside the allowed safe fixes.
- P1 — `clone/recon.js:18`, `clone/recon.js:49` — navigation errors are swallowed — deferred because this behavior change was not one of the allowed safe fixes.
- P1 — `clone/recon.js:18`, `clone/recon.js:49` — recon validates production `https://www.hormuzy.com/` instead of the checked-out static page — deferred because changing test target/workflow is outside the safe-fix list.
- P1 — `clone/recon/*.png`, `clone/recon/*.webm` — generated screenshots/videos are committed as source — deferred because deleting or relocating artifacts is outside the safe-fix list.


---

# Consensus — jill-website
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env:31` / `.env:32`: Google API keys are committed. Rotate them, restrict by domain/API, remove `.env` from git history, keep only `.env.example`.
- `src/server.ts:23` / `src/server.ts:24` / `src/middleware/auth.ts:12` / `views/admin.ejs:416`: production auth falls back to hardcoded passwords and the admin UI embeds `admin:admin123`. Require env secrets at boot; remove client-side Basic Auth; use server sessions.
- `src/routes/activities.ts:27` / `src/routes/activities.ts:144` / `src/routes/activities.ts:186` / `src/routes/activities.ts:304`: create/update/delete/bulk-import activity routes have no admin auth beyond site-wide Basic Auth. Mount mutating routes behind `requireAuth` or split public read vs admin write routers.
- `src/routes/linkValidation.ts:230` / `src/services/linkValidationService.ts:23`: `/check-url` fetches arbitrary URLs and follows redirects, enabling SSRF/internal probing. Restrict schemes to http/https, block private/link-local IPs, and allowlist external domains if possible.
- `src/migrations/002_create_cms_tables.sql:13` / `src/migrations/002_create_page_content.sql:2`: duplicate `002` migrations create incompatible `page_content` schemas; fresh migrations can fail or leave columns the model does not use. Collapse/renumber migrations and reconcile `page_title/gallery_images` vs `title/images`.


## Round-1 applied
## Applied
- `src/server.ts:52` — added `requireEnv()` and fail-fast checks for `SITE_USERNAME`, `SITE_PASSWORD`, `SESSION_SECRET`, and `ADMIN_PASSWORD` — removes real fallback secrets from source.
- `src/server.ts:62` — changed site Basic Auth to use required env credentials only — prevents fallback to the hardcoded password.
- `src/server.ts:69` — changed session middleware to use required `SESSION_SECRET` only — removes the hardcoded session-secret fallback.
- `src/middleware/auth.ts:10` — changed admin auth to require `ADMIN_PASSWORD` from env — removes the `admin123` fallback.
- `.env.example:21` — replaced the admin password example with placeholders for site/admin/session secrets — documents required env without adding real secrets.
- `src/routes/activities.ts:29` / `src/routes/activities.ts:154` — added missing `req.body` guards for create/update — malformed requests now return 400 instead of falling into handler errors.
- `src/routes/inquiries.ts:17` — added missing `req.body` guard — malformed inquiry posts now return 400.
- `src/routes/linkValidation.ts:232` — added missing `req.body` guard for manual URL checks — malformed requests now return 400.
- `src/routes/admin.ts:110` — added missing `req.body` guard for page updates — malformed admin update requests now return 400.
- `src/routes/unsubscribe.ts:3` / `src/routes/unsubscribe.ts:98` — escaped the email query before rendering HTML — prevents reflected HTML injection.
- `src/services/emailService.ts:30` / `src/services/emailService.ts:94` — escaped inquiry fields before inserting them into HTML email — prevents submitted fields from becoming email HTML.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
src/middleware/auth.ts:10 — admin password now comes from `ADMIN_PASSWORD`, but the admin UI still sends `admin:admin123`, so CMS fetch/upload/save calls 403 unless production keeps the old weak password — severity P1  
ecosystem.config.js:11 — PM2 production env only sets `NODE_ENV` and `PORT`; `src/server.ts:62`-`65` now require `SITE_USERNAME`, `SITE_PASSWORD`, `SESSION_SECRET`, and `ADMIN_PASSWORD`, so deploys relying on this config will fail at startup — severity P1


## Round-2 applied
## Round 2 Applied
- `views/admin.ejs:414` — removed the hardcoded `Authorization: Basic admin:admin123` header from the page-load API fetch and set same-origin credentials — fixes CODEX_REREVIEW missed call-site / P1 broken admin UI auth.
- `views/admin.ejs:475` — removed the hardcoded `Authorization: Basic admin:admin123` header from image uploads and set same-origin credentials — fixes CODEX_REREVIEW missed call-site / P1 broken admin UI auth.
- `views/admin.ejs:497` — removed the hardcoded `Authorization: Basic admin:admin123` header from page saves, kept JSON content type, and set same-origin credentials — fixes CODEX_REREVIEW missed call-site / P1 broken admin UI auth.


## Still deferred for Steve
- P0 — `.env:31` / `.env:32` — committed Google API keys — rotation, restriction, and history cleanup require owner access.
- P0 — `views/admin.ejs:416` — client-side Basic Auth embeds `admin:admin123` — proper fix requires auth/session redesign, not a safe surgical patch.
- P0 — `src/routes/activities.ts:27` / `src/routes/activities.ts:144` / `src/routes/activities.ts:304` — mutating activity routes lack admin auth — route authorization changes are broader behavior changes.
- P0 — `src/routes/linkValidation.ts:230` / `src/services/linkValidationService.ts:23` — `/check-url` can SSRF/probe internal URLs — safe domain/IP policy needs owner-approved allowlist.
- P0 — `src/migrations/002_create_cms_tables.sql:13` / `src/migrations/002_create_page_content.sql:2` — duplicate incompatible migrations — migrations/DDL are explicitly out of scope.
- P1 — `src/server.ts:97` / `views/admin.ejs:414` — admin API mount and UI fetch paths drift — endpoint alignment affects admin routing and was not on the safe list.
- P1 — `views/admin-login.ejs:201` — login posts to a missing route — session/login wiring is broader auth work.
- P1 — `src/server.ts:69` — site-wide Basic Auth hides public pages — public access policy needs Steve.
- P1 — `src/routes/unsubscribe.ts:10` / `src/server.ts:90` — unsubscribe router is not mounted and state is not persisted — persistence/routing behavior needs owner decision.
- P1 — `src/routes/inquiries.ts:21` — invalid date strings can reach the DB — validation semantics were not on the safe list.
- P1 — `src/routes/inquiries.ts:30` — email failures have no durable retry/alert — needs queue/status design.
- P1 — `src/routes/properties.ts:15` / `src/routes/inquiries.ts:78` — pagination accepts invalid values — query behavior change deferred.


---

# Consensus — Ken
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `src/lib/auth.js:4`, `src/lib/auth.js:6`, `src/lib/db.js:4`: hardcoded JWT secret, admin password, and Postgres password in source; compromise of repo compromises app and DB. Move to env/secret manager, rotate all exposed secrets.
- `kalshi-dash/server.js:25`, `kalshi-dash/server.js:130`, `kalshi-dash/server.js:351`, `kalshi-dash/server.js:4875`: leaked Slack webhook, Gemini API key, and two DB URLs. Revoke/rotate immediately; load from env only.
- `src/app/api/markets/route.js:7`, `src/app/api/risk/route.js:7`, `src/app/api/trades/route.js:7`, `src/app/api/workers/route.js:5`: root API routes do not call `authenticateRequest`; client-side dashboard checks are bypassable. Add server-side auth middleware to every non-login route.
- `src/app/api/trades/route.js:25-57`: `paper_order` trusts price/size/side/outcome and writes order, trade, bankroll, and risk state without validation or transaction. Validate numeric ranges/enums and wrap writes in one DB transaction.
- `react-dash/server.js:66-76`, `kalshi-dash/server.js:400-410`: session cookies are unsigned base64 JSON; anyone can forge `{authenticated:true}`. Replace with signed JWT or server-side sessions.
- `react-dash/server.js:152-164`, `kalshi-dash/server.js:589-600`: Google OAuth allows any verified Google account when `ALLOWED_EMAILS` is unset. Fail closed unless explicit owner email allowlist is configured.


## Round-1 applied
## Applied
- `src/lib/auth.js:4` — added required env lookup; `JWT_SECRET` and `ADMIN_PASSWORD` now fail fast when missing — removes hardcoded JWT/admin secrets from source.
- `src/lib/db.js:3` — added required env lookup and changed the Postgres connection to `DATABASE_URL` — removes the hardcoded DB password from source.
- `react-dash/server.js:15` — added required env lookup; auth password and DB URL now come from `ADMIN_PASSWORD` and `DATABASE_URL` — removes exposed credentials from source.
- `kalshi-dash/server.js:17` — added required env lookup; auth password, Gemini key, main DB URL, and Ken DB URL now come from env — removes exposed credentials from source.
- `kalshi-dash/server.js:33` — changed Slack webhook to optional `SLACK_WEBHOOK_URL` — removes leaked webhook from source while preserving disabled Slack behavior.
- `kalshi-dash/server.js:3554` — removed browser-embedded Basic Auth credentials and switched API calls to same-origin credentials — prevents exposing the admin password to clients.
- `.env.example:1` — added placeholder-only env variables — documents required config without writing real secrets.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`kalshi-dash/server.js:138` — missing `GEMINI_API_KEY` now aborts process startup even though `geminiAnalyze`/`aiEnhanceSignal` are designed to degrade by returning the original signal when AI fails — severity P1  
`kalshi-dash/server.js:3554` — replacing hardcoded Basic Auth with cookie-only fetches breaks direct `/inc` access without a prior session; the page is public at `kalshi-dash/server.js:4814`, but its API calls are guarded at `kalshi-dash/server.js:4793` — severity P1  
`.env.example:1` — documents required envs, but `react-dash/server.js:15` and `kalshi-dash/server.js:17` do not load `.env`; local/manual `node server.js` starts still fail unless envs are exported externally — severity P2  


## Round-2 applied
## Round 2 Applied
- `kalshi-dash/server.js:138` — made `GEMINI_API_KEY` optional instead of startup-required — fixes rereview finding `CODEX_REREVIEW.md:14` that missing Gemini config newly aborted startup despite graceful AI fallback behavior.
- `kalshi-dash/server.js:142` — added an early no-key return in `geminiAnalyze()` — completes rereview recommendation `CODEX_REREVIEW.md:25` to degrade without Gemini rather than failing the service.
- `kalshi-dash/server.js:4817` — added an auth gate before serving `/inc` HTML — fixes rereview broken finding `CODEX_REREVIEW.md:15` and missed call-site `CODEX_REREVIEW.md:19` where the page shell was public while its data APIs were authenticated.


## Still deferred for Steve
- `P0` — `src/lib/auth.js:4`, `src/lib/auth.js:14`, `src/lib/db.js:12`, `kalshi-dash/server.js:33`, `kalshi-dash/server.js:138`, `kalshi-dash/server.js:359`, `kalshi-dash/server.js:4882` — exposed secrets must be revoked/rotated — rotation requires owner/provider access.
- `P0` — `src/app/api/markets/route.js:7`, `src/app/api/risk/route.js:7`, `src/app/api/trades/route.js:7`, `src/app/api/workers/route.js:5` — API routes lack server-side auth — broader auth behavior change, not in safe autonomous list.
- `P0` — `src/app/api/trades/route.js:25` — paper order validation and transaction safety missing — trading/business logic change, not safe without tests and owner review.
- `P0` — `react-dash/server.js:66`, `kalshi-dash/server.js:400` — unsigned forgeable session cookies — replacing session design is outside the allowed safe fixes.
- `P0` — `react-dash/server.js:152`, `kalshi-dash/server.js:589` — Google OAuth fails open when `ALLOWED_EMAILS` is unset — access policy change needs Steve confirmation.
- `P1` — `src/app/api/trades/route.js:47` — bankroll updates can race — requires transaction/locking design, not a safe patch.
- `P1` — `src/workers/cron-runner.js:24` — cron jobs can overlap — scheduler/locking behavior needs design confirmation.
- `P1` — `src/app/api/markets/route.js:79` — ingest errors are hidden behind success — API contract change deferred.
- `P1` — `react-dash/server.js:43` — wildcard CORS on authenticated API — deployment origin policy needed.
- `P1` — `src/lib/risk.js:16` — dynamic SQL columns are not whitelisted — internal API hardening deferred because not in safe list.
- `P1` — `next.config.js:3` — strict mode disabled and ESLint ignored — build policy change deferred.


---

# Consensus — lawyer-directory-builder
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env:1`, `.env:9-14`: DB/admin/Stripe secrets are present in the project tree; compromise means admin takeover and live charges. Rotate secrets, remove `.env`, commit `.env.example`.
- `src/server/index.ts:19`, `src/server/upgrade.ts:242-249`: global `express.json()` runs before Stripe `express.raw()`, so webhook signature verification will receive parsed JSON, not raw bytes. Mount `/webhooks/stripe` raw parser before JSON middleware.
- `src/server/upgrade.ts:173-199`: Stripe `success_url`/`cancel_url` are built from `Host` / `x-forwarded-host`; hostile headers can create payment sessions redirecting to attacker-controlled domains. Use fixed `PUBLIC_BASE_URL`.
- `src/server/portal.ts:388-395`: login has no rate limit, lockout, or audit trail; leaked/admin emails can be brute-forced. Add per-IP+email throttling and failed-login logging.
- `package-lock.json:1544-1545`, `src/server/index.ts:34-181`: Express 4 async route errors are not wrapped; DB errors can become unhandled rejections/hung requests. Add async handler wrapper and central error middleware.
- `src/server/leads.ts:186-227`, `src/server/directory.ts:468-484`: public lead/inquiry writes have no rate limit, CAPTCHA, or duplicate suppression; easy DB/admin spam. Add throttling and spam controls.


## Round-1 applied
## Applied
- .env.example:1 - added placeholder-only environment template - documents required keys without copying real secrets from `.env`.
- src/server/portal.ts:352 - added malformed/missing body guard on signup - avoids a 500 when `req.body` is absent.
- src/server/portal.ts:391 - added malformed/missing body guard on login - avoids a 500 when `req.body` is absent.
- src/server/portal.ts:534 - added malformed/missing body guard on admin plan updates - avoids a 500 when `req.body` is absent.
- src/server/leads.ts:186 - added malformed/missing body guard on public lead submission - avoids a 500 when `req.body` is absent.
- src/server/leads.ts:337 - added malformed/missing body guards on admin lead updates - avoids a 500 when `req.body` is absent.
- src/server/directory.ts:468 - added malformed/missing body guard on directory inquiries - avoids a 500 when `req.body` is absent.
- src/server/upgrade.ts:149 - added malformed/missing body guard on checkout creation - avoids a 500 when `req.body` is absent.
- src/server/upgrade.ts:314 - added malformed/missing body guard on order status updates - avoids a 500 when `req.body` is absent.
- src/enrich/site_audit.ts:268 - wrapped Playwright browser lifecycle in `try/finally` - ensures the browser closes on worker failures.
- src/scripts/click_through.ts:30 - wrapped Playwright browser lifecycle in `try/finally` - ensures smoke-test browser cleanup.
- src/scripts/probe_calbar.ts:14 - wrapped Playwright browser lifecycle in `try/finally` - ensures probe browser cleanup.
- src/scripts/create_admin.ts:18 - replaced CLI password argument with `CREATE_ADMIN_PASSWORD`/`ADMIN_PASSWORD` env lookup - avoids exposing the password in process args.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
none observed.


## Round-2 applied
## Round 2 Applied
- src/server/data_market.ts:321 — added malformed/missing body guard before reading `b.kind` and other body fields — fixes CODEX_REREVIEW.md "Missed call-sites" finding for `POST /data/checkout`, where absent/non-object `req.body` could 500.


## Still deferred for Steve
- P0 - .env:1, .env:9-14 - real secrets are in the project tree - rotation/removal requires Steve; `.env` was left untouched.
- P0 - src/server/index.ts:19, src/server/upgrade.ts:242 - Stripe raw webhook parser is mounted after global JSON parser - not in safe list.
- P0 - src/server/upgrade.ts:173 - checkout redirects trust forwarded host headers - needs a fixed `PUBLIC_BASE_URL` decision.
- P0 - src/server/portal.ts:388 - login lacks rate limit/audit trail - auth policy change, not in safe list.
- P0 - src/server/index.ts:34 - async Express handlers lack central error wrapping - broad routing change, not in safe list.
- P0 - src/server/leads.ts:186, src/server/directory.ts:468 - public write endpoints lack spam controls - product/security policy decision.
- P1 - package.json:10 - scripts reference missing files - deferred because restoring/removing ops commands was not in safe list.
- P1 - package-lock.json:971 - Express runtime/types mismatch - package/version change is out of scope.
- P1 - src/scripts/run_overnight.ts:88 - required child job failures do not fail orchestrator - behavior change outside safe list.
- P1 - src/scripts/migrate.ts:28 - migrations not transactional - migration/DDL area is explicitly deferred.
- P1 - src/enrich/discover_websites.ts:127 - CLI `LIMIT` validation/parameterization - not in safe list.
- P1 - src/enrich/firm_website_contacts.ts:127 - duplicate race needs unique indexes/`ON CONFLICT` - schema change is explicitly deferred.
- P1 - src/lib/match_firms.ts:72 - repeated audit subqueries may slow down - query refactor outside safe list.
- P1 - package-lock.json:1544 - existing typecheck failures - not changed; tests/typecheck were not run by request.


---

# Consensus — Letsbegin
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env.local:1`, `CLAUDE.md:5-6`, `CLAUDE.md:24-27`, `lib/gemini-image.ts:7`, `app/api/prd/generate/route.ts:8`: live API keys/passwords are in repo/docs. Rotate keys/passwords, purge history, require env vars with no fallbacks.
- `middleware.ts:6-15`: `/api/ralph`, `/api/processes`, `/api/live`, `/api/url-info` are public by prefix. Remove these from `publicPaths`; require auth and CSRF for every mutating/admin route.
- `lib/auth.ts:7-10`, `app/api/auth/login/route.ts:54-60`, `middleware.ts:68-80`: sessions are unsigned base64 JSON and cookies are `secure:false`; anyone can forge `{authenticated:true}`. Use signed/encrypted session cookies, `secure:true`, env-only secrets.
- `app/api/ralph/run/route.ts:57-63`, `app/api/ralph/run/route.ts:89-166`, `app/api/ralph/run/route.ts:168-237`: public endpoint accepts arbitrary `projectPath`/story data, writes files, `chown -R`s, creates shell scripts, and spawns them. Restrict project paths to an allowlist and remove public access before reuse.
- `app/api/processes/start/route.ts:83-194`: root-running script path uses unsanitized PRD fields and invokes `claude --dangerously-skip-permissions`. Disable this route or run in a locked sandbox with strict input schema and no root HOME.
- `app/api/processes/stop/route.ts:17-36`, `app/api/live/check/route.ts:123-127`: public process-control routes can kill arbitrary PIDs or pkill by injected project/action data. Authenticate, validate numeric PID ownership, remove shell interpolation.
- `app/api/url-info/route.ts:26-43`: public SSRF fetches any `http*` URL, including localhost/internal services. Block private IPs, redirects to private ranges, non-HTTP(S), and add rate limits.
- `app/api/quickmode/route.ts:4-6`: hardcoded Postgres production credentials. Move to `DATABASE_URL`, rotate DB password, and use least-privileged DB user.


## Round-1 applied
## Applied
- `.env.example:1` — added placeholder-only env keys for Gemini, auth, admin scan, database, Ralph, and Shopify — documents required configuration without real secrets.
- `lib/auth.ts:8` — replaced hardcoded auth/session fallbacks with required env lookup — removes fallback credentials from source.
- `lib/gemini-image.ts:7`, `app/api/prd/generate/route.ts:8`, `app/api/prd/clarify/route.ts:4`, `app/api/ralph/convert/route.ts:6`, `app/api/claude/suggest/route.ts:6`, `app/api/url-info/route.ts:4`, `app/api/processes/analyze/route.ts:6`, `app/api/processes/fix/route.ts:7` — replaced Gemini key fallbacks with fail-fast env checks — removes live API key from app source.
- `scripts/ralph-llm-runner.js:45` — removed Gemini fallback key and fails fast if no Ralph/Gemini env key exists — removes live API key from runner.
- `app/api/quickmode/route.ts:4` — replaced hardcoded Postgres URL with required `DATABASE_URL` — removes DB password from API source.
- `app/api/projects/scan/route.ts:6` — replaced admin-password fallback with required `ADMIN_PASSWORD` — removes admin password from source.
- `components/PRDGeneratorStep.tsx:339` — removed hardcoded scan admin header from client fetch — stops exposing the admin key in browser code.
- `CLAUDE.md:6` — replaced real auth/API examples with env-based examples — removes documented secrets.
- `app/api/prd/generate/route.ts:73`, `app/api/url-info/route.ts:31`, `app/api/quickmode/route.ts:32` — added invalid-JSON guards returning 400 — avoids malformed bodies falling through to 500.
- `sync_brands_page.js:16`, `test_brands.js:16`, `fix_rew_products.js:7`, `nina_full_monty.js:24`, `add_ai_desc_thibaut.js:5`, `ani_full_monty.js:17`, `croppy_glm.js:27`, `fix_dwpx_images.js:7`, `fix_x_mfr.js:3`, `glitter_full_monty.js:21`, `nina_crop.js:19`, `nina_names_colors.js:5`, `retro_full_monty.js:24`, `sequin_colors_titles.js:6`, `sequin_full_monty.js:15`, `sequin_monty_noimages.js:6` — replaced script API tokens/keys/DB URLs with env variables and fail-fast checks — removes live secrets from non-migration scripts.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`scripts/ralph-llm-runner.js:47` — requires `GEMINI_API_KEY_RALPH`/`GEMINI_API_KEY` even when `RALPH_LLM` defaults to `ollama` at `scripts/ralph-llm-runner.js:110`; this breaks documented/default Ollama runs without Gemini credentials — severity P1


## Round-2 applied
## Round 2 Applied
- `scripts/ralph-llm-runner.js:45` — removed the module-scope Gemini env failure and kept the key lookup passive — fixes CODEX_REREVIEW "STATUS broken" / P1 new issue where default Ollama runs required Gemini credentials.
- `scripts/ralph-llm-runner.js:110` — added the Gemini env failure inside `callGemini()` only — follows CODEX_REREVIEW recommendation to require `GEMINI_API_KEY_RALPH`/`GEMINI_API_KEY` only when the Gemini backend is actually used.
- `app/api/prd/clarify/route.ts:75` — wrapped `request.json()` with a local 400 invalid-JSON response — fixes CODEX_REREVIEW missed call-site at line 75.
- `app/api/ralph/convert/route.ts:141` — wrapped `request.json()` with a local 400 invalid-JSON response — fixes CODEX_REREVIEW missed call-site at line 141.
- `app/api/claude/suggest/route.ts:130` — wrapped `request.json()` with a local 400 invalid-JSON response and kept safe defaults for destructured fields — fixes CODEX_REREVIEW missed call-site at line 130.
- `app/api/processes/analyze/route.ts:33` — wrapped `request.json()` with a local 400 invalid-JSON response — fixes CODEX_REREVIEW missed call-site at line 33.
- `app/api/processes/fix/route.ts:36` — wrapped `request.json()` with a local 400 invalid-JSON response — fixes CODEX_REREVIEW missed call-site at line 36.


## Still deferred for Steve
- P0 — `.env.local:1` — live secrets may remain in local env — left alone per instruction; rotate/purge history needs Steve.
- P0 — `scrape_dg_specs.js:12`, `migrate_global_to_custom.js:29` — remaining hardcoded DB/Shopify secrets — deferred because these are DDL/migration-adjacent scripts.
- P0 — `middleware.ts:6` — admin/process/API prefixes are public — auth/CSRF redesign is beyond safe fixes.
- P0 — `lib/auth.ts:8`, `app/api/auth/login/route.ts:58`, `middleware.ts:70` — sessions are unsigned and cookie `secure` is false — needs session design change.
- P0 — `app/api/ralph/run/route.ts:57`, `app/api/processes/start/route.ts:83`, `app/api/processes/stop/route.ts:17`, `app/api/live/check/route.ts:123` — arbitrary project/process control and shell execution risks — needs sandboxing/authorization.
- P0 — `app/api/url-info/route.ts:29` — public SSRF remains — needs URL/IP validation and rate limiting.
- P1 — `app/api/files/route.ts:14`, `app/api/directory/route.ts:71`, `app/api/upload/route.ts:16` — broad path access/upload/delete guards — needs realpath allowlist design.
- P1 — `app/api/projects/scan/route.ts:12`, `app/api/projects/scan/route.ts:215` — referer trust and shell interpolation remain — needs real authorization plus exec/native API rewrite.
- P1 — `lib/sheets.ts:32`, `app/api/ralph/status/route.ts:41` — concurrent JSON state writes/GET mutation — needs locking or durable state store.


---

# Consensus — malden-house
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- [signup.js](/Users/stevestudio2/Projects/malden-house/signup.js:217): email verification is hardcoded `verified: true`; anyone can “claim” any address/email locally. Fix with server-side account creation, signed email tokens, and address ownership rules.
- [rental.js](/Users/stevestudio2/Projects/malden-house/rental.js:735): guest inquiries are saved to the visitor’s own `localStorage`, not the host’s inbox; production inquiries silently disappear. Fix with a backend endpoint/email delivery and explicit failure UI.
- [network.js](/Users/stevestudio2/Projects/malden-house/network.js:33): public source exposes Tailscale IPs, public server IPs, hostnames, service names, and ports. Remove `network.html/js` from public builds or gate behind auth/VPN-only hosting.
- [dashboard.js](/Users/stevestudio2/Projects/malden-house/dashboard.js:30), [browse.js](/Users/stevestudio2/Projects/malden-house/browse.js:35), [rental.js](/Users/stevestudio2/Projects/malden-house/rental.js:374): trusted `localStorage`/geocoder fields are rendered through template `innerHTML`; malicious stored records can execute markup/script-like payloads. Build DOM nodes with `textContent` or sanitize all interpolated fields.
- [signup.js](/Users/stevestudio2/Projects/malden-house/signup.js:263): demo seed includes Steve’s real-looking email/phone/address in source and UI. Replace with fake fixtures before any public repo/deploy.


## Round-1 applied
## Applied

- `dashboard.js:17` — added a tiny HTML/attribute escaping helper — needed before using template `innerHTML` with stored records.
- `dashboard.js:39` — escaped rental card fields, image attributes, slugs, chips, address text, and host names — prevents malicious `localStorage` records from injecting markup.
- `dashboard.js:149` — escaped inquiry inbox names, email text, dates, events, street labels, and data attributes — prevents stored inquiry fields from rendering as HTML.
- `dashboard.js:193` — escaped photo picker labels/values and photo `src` attributes — keeps stored rental/photo metadata inside attributes/text.
- `browse.js:7` — added the same escaping helper — needed for the browse card renderer.
- `browse.js:44` — escaped browse card image attributes, chip text, address/city fields, and host names; encoded the map query — prevents stored rental data injection.
- `rental.js:35` — added HTML/attribute escaping helpers — needed for dynamic rental templates.
- `rental.js:89` — escaped trailer and cover fields from stored address, host, and venue data — prevents injected markup during trailer playback.
- `rental.js:329` — escaped the property card subline while preserving its `<br>` layout — prevents address/county markup injection.
- `rental.js:383` — escaped nearest-venue headline, name, sports, drive minutes, and date chip fields — prevents venue string injection in the event grid.
- `rental.js:503` — escaped drive-meta minutes, contact heading host name, footer host name, countdown banner text, closest-event text, and inquiry thank-you fields — closes remaining touched `innerHTML` paths fed by stored/user data.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`rental.js:35` — `escapeHtml(s)` uses `String(s || "")`, so numeric `0` becomes empty; applied numeric call-sites like `rental.js:503` can render blank drive minutes — P2


## Round-2 applied
## Round 2 Applied

- `dashboard.js:17` — changed `escapeHtml` to preserve numeric `0`, and added `formatMoney()` — fixes rereview concern that `String(s || "")` collapses zero values.
- `dashboard.js:62` — escaped stored `r.price.mid` before card `innerHTML` insertion — fixes rereview missed call-site `dashboard.js:59`.
- `browse.js:7` — changed `escapeHtml` to preserve numeric `0`, and added `formatMoney()` — fixes rereview concern that `String(s || "")` collapses zero values.
- `browse.js:66` — escaped stored `r.price.mid` before browse card `innerHTML` insertion — fixes rereview missed call-site `browse.js:63`.
- `rental.js:35` — changed `escapeHtml` to preserve numeric `0`, and added `formatMoney()` — fixes rereview concern/new issue around numeric display.
- `rental.js:142` — escaped stored `price.mid` in trailer outro pricing — fixes rereview missed call-site `rental.js:139`.
- `rental.js:424` — passed escaped `address.city` into dynamic guide heading templates while preserving intended `<br>` markup — fixes rereview missed call-site `rental.js:422`.
- `rental.js:459` — passed escaped `address.city` into dynamic history heading templates while preserving intended `<br>` markup — fixes rereview missed call-site `rental.js:459`.
- `rental.js:622` — escaped stored `price.mid`, `price.low`, `price.high`, and `baseline.base` in the price card — fixes rereview missed call-site `rental.js:619`.
- `signup.js:147` — added HTML/attribute escaping helpers for signup dynamic HTML — supports the rereview signup and autocomplete findings.
- `signup.js:240` — escaped submitted/geocoded signup status fields before `setStatus(...innerHTML...)` — fixes rereview missed call-site `signup.js:226`.
- `signup.js:328` — escaped Nominatim autocomplete `data-val`, line 1, and line 2 fields before `innerHTML` insertion — fixes rereview missed call-site `signup.js:312`.


## Still deferred for Steve

- P0 — `signup.js:217` — email verification is hardcoded as `verified: true` — requires backend accounts, signed email tokens, and ownership rules.
- P0 — `rental.js:735` — guest inquiries save only to visitor `localStorage` — needs backend/email delivery and failure UX.
- P0 — `network.js:33` — network viewer exposes IPs, hostnames, service names, and ports — removal/gating needs a deploy/access decision.
- P0 — `signup.js:263` — demo seed contains Steve's real-looking contact/address fixture — content/PII cleanup needs Steve.
- P1 — `signup.js:219` — slug collisions overwrite rentals — durable handling belongs with server-side claim rules.
- P1 — `signup.js:9`, `rental.js:3` — schema mismatch wipes stored data — migration/backup behavior touches user data.
- P1 — `dashboard.js:220` — photo uploads can exceed `localStorage` quota — storage migration is larger than a surgical safe fix.
- P1 — `schedule.js:5` — venue data is duplicated — shared-data refactor is outside the safe list.
- P1 — `test-e2e.js:12` — smoke test depends on live Nominatim and sleeps — test changes were out of scope.
- P1 — `network.js:28` — `no-cors` polling can show false green health — needs a same-origin proxy or removal decision.


---

# Consensus — Norma
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env.local:1`, `.env.local:3-5`, `.env.local:14`, `.env.local:21-22`, `CLAUDE.md:12-13`, `README.md:21`: live-looking DB/admin/cron/session/Gemini/Gmail secrets are committed/documented. Rotate all exposed credentials, remove secrets from repo/docs, purge history if shared.
- `middleware.ts:55-89` + `app/api/social/ai-image/route.ts:20`, `app/api/social/bulk-schedule/route.ts:66`, `app/api/social/bulk-schedule/route.ts:88`, `app/api/social/posts/approve/route.ts:10`, `app/api/social/posts/pending/route.ts:10`: middleware only checks token structure/expiry, while these routes do no `requireRole`; forged cookies can generate images, schedule/approve/read social posts. Add `requireRole` to every non-public route or crypto-verify in middleware.
- `app/api/pulse/petitions/route.ts:111-182`: unauthenticated public POST creates immediately active petitions with no rate limit, moderation, captcha, or abuse controls. Default to draft/moderation and add IP/user rate limiting.
- `app/api/pulse/petitions/[id]/sign/route.ts:13-44`, `app/api/pulse/petitions/[id]/sign/route.ts:51-52`: public signing has no rate limit and does not check `allow_signatures`; any active petition can be spam-signed. Check `allow_signatures`, add rate limiting/captcha/email verification.
- `app/api/nonprofit/enrich/route.ts:179-184`, `app/api/nonprofit/enrich/route.ts:227-235`, `app/api/email-analyzer/fetch-url/route.ts:202-215`, `lib/social-crawl.ts:111-117`: server-side URL fetch accepts arbitrary HTTP(S), follows redirects, and does not block private/link-local IPs. Add SSRF guard: DNS/IP validation, redirect revalidation, size limits, and deny internal ranges.
- `lib/auth.ts:5-8`, `lib/auth.ts:179-189`, `lib/cron-auth.ts:8`, `app/api/webhooks/pulse-topic/route.ts:6`: secrets fall back to predictable values and auth cookie is only `Secure` when `USE_HTTPS=true`. Fail closed in production and always set `Secure` behind HTTPS.


## Round-1 applied
## Applied
- `lib/auth.ts:5` — removed the generated fallback session secret and now fail fast when `SESSION_SECRET` is missing — prevents predictable session signing.
- `app/api/webhooks/pulse-topic/route.ts:6` — removed the hardcoded webhook secret fallback and now fail fast when `WEBHOOK_SECRET` is missing — prevents accepting a public source secret.
- `lib/detect-mentions.ts:20` — removed the hardcoded Gemini API key fallback and now requires `GEMINI_API_KEY` before calling Gemini — prevents leaking or using an embedded API key.
- `app/api/drafts/auto-generate/route.ts:12` — replaced a hardcoded Gemini API key with `GEMINI_API_KEY` plus a fail-fast check — removes a real secret from source.
- `app/api/social/ai-compose/route.ts:4` — replaced a hardcoded Gemini API key with `GEMINI_API_KEY` plus a fail-fast check — removes a real secret from source.
- `.env.example:1` — added a placeholder `SESSION_SECRET` value — documents required config without committing a real secret.
- `.env.example:2` — added a placeholder `WEBHOOK_SECRET` value — documents required config without committing a real secret.
- `.env.example:3` — added a placeholder `GEMINI_API_KEY` value — documents required config without committing a real secret.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`app/api/webhooks/pulse-topic/route.ts:11` — same route now fail-fasts for `WEBHOOK_SECRET` but still builds Gemini URL with `GEMINI_API_KEY || ''`; missing Gemini config becomes a runtime 502 path instead of the documented fail-fast behavior — P2


## Round-2 applied
## Round 2 Applied
- `app/api/webhooks/pulse-topic/route.ts:8` — added a `GEMINI_API_KEY` guard and builds the Gemini URL only when called — fixes CODEX_REREVIEW “New issues introduced” concern about `GEMINI_API_KEY || ''`.
- `app/api/webhooks/pulse-topic/route.ts:29` — moved missing `WEBHOOK_SECRET` handling into the route response instead of module-scope throw — addresses CODEX_REREVIEW “concern” on this patch.
- `scripts/detect-news-mentions.ts:16` — removed the hardcoded Gemini fallback and require `GEMINI_API_KEY` for Gemini calls — fixes CODEX_REREVIEW “Missed call-sites”.
- `scripts/detect-news-mentions.ts:185` — fail fast when `--gemini` is used without `GEMINI_API_KEY` — keeps the missed-call-site fix from being swallowed by the fallback catch path.
- `scripts/enrich-news-ai.js:18` — removed the hardcoded Gemini key and require `GEMINI_API_KEY` before requests — fixes CODEX_REREVIEW “Missed call-sites”.
- `agents/shared/ai.js:12` — removed the hardcoded Gemini fallback and require `GEMINI_API_KEY` — fixes CODEX_REREVIEW “Missed call-sites”.
- `app/api/outreach/generate/route.ts:8` — replaced empty-key Gemini URL fallback with a guarded URL helper — fixes CODEX_REREVIEW “Missed call-sites”.
- `app/api/outreach-pipeline/generate/route.ts:7` — replaced empty-key Gemini URL fallback with a guarded URL helper — fixes CODEX_REREVIEW “Missed call-sites”.
- `app/api/pipeline/generate/route.ts:8` — replaced empty-key Gemini URL fallback with a guarded URL helper — fixes CODEX_REREVIEW “Missed call-sites”.
- `app/api/statements/generate/route.ts:8` — replaced empty-key Gemini URL fallback with a guarded URL helper — fixes CODEX_REREVIEW “Missed call-sites”.
- `app/api/agents/petition/generate/route.ts:5` — replaced empty-key Gemini URL fallback with a guarded URL helper — fixes CODEX_REREVIEW “Missed call-sites”.
- `app/api/agents/advocacy/generate/route.ts:5` — replaced empty-key Gemini URL fallback with a guarded URL helper — fixes CODEX_REREVIEW “Missed call-sites”.


## Still deferred for Steve
- P0 — `.env.local:1`, `.env.local:3-5`, `.env.local:14`, `.env.local:21-22`, `CLAUDE.md:12-13`, `README.md:21` — live-looking secrets are committed/documented — requires owner rotation, history decision, and careful secret cleanup.
- P0 — `middleware.ts:55`, `app/api/social/ai-image/route.ts:20`, `app/api/social/bulk-schedule/route.ts:66`, `app/api/social/posts/approve/route.ts:10`, `app/api/social/posts/pending/route.ts:10` — forged cookies can reach social routes — auth model change was outside safe fixes.
- P0 — `app/api/pulse/petitions/route.ts:111` — public petition creation lacks abuse controls — moderation/rate-limit behavior needs product decision.
- P0 — `app/api/pulse/petitions/[id]/sign/route.ts:13` — public signing lacks rate limit and `allow_signatures` enforcement — abuse-control design needed.
- P0 — `app/api/nonprofit/enrich/route.ts:179`, `app/api/email-analyzer/fetch-url/route.ts:202`, `lib/social-crawl.ts:111` — arbitrary URL fetch can SSRF — needs a shared network guard and testing.
- P0 — `lib/cron-auth.ts:8`, `lib/auth.ts:179` — cron-secret fallback/cookie Secure behavior remains — not a literal hardcoded secret fallback; may affect deployment routing.
- P1 — `next.config.ts:5` — production ignores TypeScript/ESLint errors — could break builds if changed blindly.
- P1 — `app/api/auth/google/route.ts:15`, `app/api/auth/google/callback/route.ts:10` — OAuth lacks state validation — auth flow change needs testing.
- P1 — `app/api/nonprofit/signup/route.ts:170` — duplicate email race/unique index missing — database schema changes are forbidden.
- P1 — `db/schema.sql:252` — schema bootstrap issue — `.sql` edits are forbidden.
- P1 — `app/api/social/bulk-schedule/route.ts:108` — bulk schedule has no request size caps — validation behavior change outside safe list.
- P1 — `app/api/email-analyzer/fetch-url/route.ts:241` — response body read before truncation — network/body streaming refactor outside safe list.


---

# Consensus — professional-directory
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- [agents/api-agent/server.js](/Users/stevestudio2/Projects/professional-directory/agents/api-agent/server.js:18), [agents/ingest-agent/server.js](/Users/stevestudio2/Projects/professional-directory/agents/ingest-agent/server.js:47): no auth on mutating/control endpoints; localhost CSRF or any proxy exposure can trigger imports, opt-outs, or CRM edits. Add shared auth middleware, Origin checks, and protect all `POST` routes.
- [agents/ingest-agent/server.js](/Users/stevestudio2/Projects/professional-directory/agents/ingest-agent/server.js:55), [agents/crawler-agent/server.js](/Users/stevestudio2/Projects/professional-directory/agents/crawler-agent/server.js:48): request body can override child-process env, including DB/source URLs. Remove arbitrary env merging; allowlist only safe knobs like `LIMIT`.
- [agents/api-agent/server.js](/Users/stevestudio2/Projects/professional-directory/agents/api-agent/server.js:166): API references `suggested_domain`, `domain_available`, `starred`, `contacted_at`, `is_chain`, `lead_score`; schema lacks them at [db/schema.sql](/Users/stevestudio2/Projects/professional-directory/db/schema.sql:63). Fresh deploy breaks org routes. Add a migration or remove those fields.


## Round-1 applied
## Applied
- None — no P0/P1 review item matched the permitted safe-fix categories without changing behavior, schema, auth policy, or operational semantics.


## Still deferred for Steve
- P0 — agents/api-agent/server.js:18 — no auth on mutating/control endpoints — deferred because adding shared auth and Origin policy is not one of the allowed safe fixes and needs owner decisions for credentials and exposure model.
- P0 — agents/ingest-agent/server.js:47 — no auth on importer control endpoint — deferred for the same auth policy reason.
- P0 — agents/ingest-agent/server.js:55 — request body can override child-process env — deferred because replacing env override behavior with an allowlist changes operator workflow and is not in the allowed safe-fix list.
- P0 — agents/crawler-agent/server.js:48 — request body can override child-process env — deferred for the same operator-workflow reason.
- P0 — agents/api-agent/server.js:166 — API references organization columns absent from checked-in schema — deferred because this requires schema/migration work, which is explicitly out of scope.
- P1 — scripts/run-all-night.sh:29 — failed stages continue and final output still says complete — deferred because fail-fast/final-status behavior is operational policy, not one of the allowed safe fixes.
- P1 — agents/ingest-agent/importers/npi.js:189 — NPI reruns can insert duplicate locations and phones — deferred because the recommended fix requires database constraints/migrations, explicitly out of scope.
- P1 — agents/ingest-agent/importers/npi.js:346 — row-level DB failures are counted as skipped and job may finish done — deferred because failure-threshold policy is not in the allowed safe-fix list.
- P1 — scripts/dedupe.js:80 — org merge updates/deletes multiple tables without a transaction — deferred because it is not in the allowed safe-fix list and affects data repair behavior.
- P1 — agents/api-agent/server.js:727 — imported website values become clickable hrefs without protocol allowlisting — deferred because changing rendered/exported public-facing HTML behavior is outside the safe-fix list and public-facing copy/HTML is out of scope.
- P1 — agents/shared/compliance.js:58 — rate limiter is in-memory and non-atomic per process — deferred because shared queue/lock design changes cross-process behavior and is not one of the allowed safe fixes.


---

# Consensus — resize-it
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- [server.py:21](/Users/stevestudio2/Projects/resize-it/server.py:21), [server.py:22](/Users/stevestudio2/Projects/resize-it/server.py:22), [.env:1](/Users/stevestudio2/Projects/resize-it/.env:1), [.env:2](/Users/stevestudio2/Projects/resize-it/.env:2): Spoonflower credentials are committed/hardcoded defaults. Rotate password, remove defaults, require env vars at startup.
- [SKILL.md:89](/Users/stevestudio2/Projects/resize-it/SKILL.md:89): Gemini API key is embedded in docs/source. Revoke it, move to secret storage, scrub history if this was ever shared.
- [server.py:270](/Users/stevestudio2/Projects/resize-it/server.py:270)-[274](/Users/stevestudio2/Projects/resize-it/server.py:274): Flask debug server binds `0.0.0.0` and advertises a public IP. Disable debug, bind localhost or put behind authenticated production WSGI.
- [server.py:29](/Users/stevestudio2/Projects/resize-it/server.py:29), [server.py:103](/Users/stevestudio2/Projects/resize-it/server.py:103), [server.py:155](/Users/stevestudio2/Projects/resize-it/server.py:155): no auth on endpoints that use stored Spoonflower credentials. Add owner auth/token gate; especially block public `/api/upload-to-spoonflower`.
- [server.py:165](/Users/stevestudio2/Projects/resize-it/server.py:165)-[167](/Users/stevestudio2/Projects/resize-it/server.py:167): URL validation is substring-only; `http://internal/?spoonflower.com` passes to Playwright. Use `urlparse`, require HTTPS and exact Spoonflower host suffix.
- [server.py:190](/Users/stevestudio2/Projects/resize-it/server.py:190)-[197](/Users/stevestudio2/Projects/resize-it/server.py:197): picks newest image from global `/tmp`; concurrent requests can resize/download the wrong customer file. Use per-request temp dirs and explicit script output paths.


## Round-1 applied
## Applied
- `server.py:23` — removed hardcoded Spoonflower credential defaults and now fails fast when `SPOONFLOWER_EMAIL` or `SPOONFLOWER_PASSWORD` is missing — prevents source fallback secrets.
- `server.py:18`, `server.py:66`, `server.py:154`, `server.py:197`, `server.py:226` — replaced hardcoded `/root/.claude/skills/resize-it` subprocess cwd values with `Path(__file__).resolve().parent` — makes scripts run from this checkout.
- `server.py:119`, `server.py:174` — replaced `request.json` with `request.get_json(silent=True)` plus a 400 response for missing or malformed JSON — avoids 500s on bad request bodies.
- `server.py:291` — changed advertised URL to localhost — removes public-IP guidance for a local tool.
- `server.py:292` — changed Flask bind from `0.0.0.0`/`debug=True` to `127.0.0.1`/`debug=False` — avoids exposing the debug server.
- `SKILL.md:89` — replaced embedded Gemini API key with `process.env.GEMINI_API_KEY` and a fail-fast check — removes the secret from docs/source.
- `.env.example:1` — added placeholder environment variables for Spoonflower credentials and Gemini API key — documents required configuration without storing real secrets.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`server.py:33` — missing Spoonflower env vars now raise during module import, so `/` and `/api/resize` cannot run without Spoonflower credentials even though local resize does not use them — P1

`server.py:23` — credentials are read only from process env; existing local `.env` will be ignored because no dotenv loader is present — P1


## POKE deep-dive (this project had a self-found regression or crash)
## Failure mode
Starting/importing `server.py` fails before `/` or `/api/resize` can run when `SPOONFLOWER_EMAIL` or `SPOONFLOWER_PASSWORD` is unset. The local resize path does not use Spoonflower credentials, but import-time validation blocks it anyway. `server.py:23-34`, `server.py:41-67`

## Root cause
`server.py:33-34` — the module raises `RuntimeError` during import if either Spoonflower env var is missing. That code runs before route handlers are usable, while the local upload/resize handler only shells out to `scripts/resize_image.py` and does not need those credentials. `server.py:23-34`, `server.py:41-67`

## Origin
REGRESSION — `CHANGES.md:2` says the recent patch removed hardcoded Spoonflower defaults and made startup fail fast when env vars are missing. `CODEX_REREVIEW.md:5` and `CODEX_REREVIEW.md:20` identify that as broken because unrelated local resize usage is blocked.

## Proposed patch
`server.py:26-34`
```diff
- missing_env = [
+ def missing_spoonflower_env():
+     return [

## Still deferred for Steve
- P0 — `.env:1` — real Spoonflower credentials may still exist locally — deferred because `.env` was explicitly left untouched and real credentials must be rotated by Steve.
- P0 — `SKILL.md:89` — exposed Gemini API key needs revocation and history scrubbing if shared — source was cleaned, but rotation/history cleanup needs Steve.
- P0 — `server.py:41`, `server.py:115`, `server.py:170` — no auth on local API endpoints that can use stored credentials — deferred because auth design/token ownership is outside the safe fix list.
- P0 — `server.py:183` — Spoonflower URL validation is substring-only — deferred because stronger host validation was not in the safe fix list.
- P0 — `server.py:208` — newest-image selection from global `/tmp` can cross requests — deferred because request-scoped temp workflow changes are broader than safe surgical fixes.
- P1 — `server.py:52` — uploaded files can overwrite the same sanitized filename — deferred because filename/session behavior was not in the safe fix list.
- P1 — `server.py:277` — download route can expose matching files in `/tmp` — deferred because allowlist/session mapping is a broader behavior change.
- P1 — `scripts/spoonflower_full_workflow.py:201` — upload success is inferred after sleep/screenshot — deferred because adding confirmed save-state assertions needs workflow testing.
- P1 — `web-interface.html:263` — frontend reads `error.message` while server returns `error` — deferred because public-facing HTML changes were out of scope.


---

# Consensus — secrets-manager
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `audit-2026-04-30.txt:1`: audit found `307` secret-looking leaks across projects/skills; current manager records the blast radius but does not remediate it. Rotate exposed keys, delete hardcoded copies, then rerun audit until clean.
- `auto-ingest.sh:31` + `auto-ingest.sh:51`: import failures are swallowed with `|| true`, then the changed hash is committed; bad/partial ingests can be silently skipped forever. Only write state after a successful import; log and exit nonzero on failure.
- `auto-ingest.sh:42-44`: every qualifying changed desktop file can background-launch `run-all-stages.sh`; no lock/idempotency guard. Add a lockfile or launchd `KeepAlive`/single-flight guard and record last fired input hash.
- `cli.js:68` + `cli.js:104`: `StrictHostKeyChecking=accept-new` while pushing secrets to remote root hosts risks trusting the wrong first-seen host. Pin host keys in `known_hosts` and fail closed.
- `routes.json:184-202`, `routes.json:301-319`, `routes.json:321-340`: prod API keys are routed directly to `root@45.61.58.125`. Use a restricted deploy user and least-privilege remote path ownership.


## Round-1 applied
## Applied
- None — no P0/P1 item in `CODEX_REVIEW.md` matched the approved autonomous safe-fix set closely enough to edit without broader operational risk.


## Still deferred for Steve
- P0 — `audit-2026-04-30.txt:1` — 307 secret-looking leaks across external projects/skills — requires secret rotation and cleanup outside this project.
- P0 — `auto-ingest.sh:31` / `auto-ingest.sh:51` — failed imports are swallowed before state hash is saved — reliability behavior change outside approved safe list.
- P0 — `auto-ingest.sh:42` — pipeline can be launched repeatedly without a lock — needs owner decision on launch semantics/idempotency.
- P0 — `cli.js:68` / `cli.js:104` — SSH accepts first-seen host keys — needs host key pinning details from Steve.
- P0 — `routes.json:198` / `routes.json:315` / `routes.json:336` — prod secrets fan out to remote `root` — needs deploy user and remote ownership changes.
- P1 — `cli.js:225` / `cli.js:257` — batch import exits on first invalid token — control-flow refactor not in approved safe list.
- P1 — `clipboard-watcher.launchd.err:1` / `supervisor.log:23` — Desktop access is blocked under launchd — needs Full Disk Access or workflow relocation.
- P1 — `clipboard-watcher.log:6` / `clipboard-watcher.sh:36` / `auto-ingest.sh:31` — inconsistent Node executable path — environment/install decision needed.
- P1 — `cli.js:33` / `cli.js:59` / `cli.js:210` — writes are non-atomic — broader file-write helper change not in approved safe list.
- P1 — `cli.js:64` — remote read SSH failure is treated as empty env — behavior change risks remote overwrite semantics.
- P1 — `clipboard-watcher.sh:60` — prefixless clipboard token classifiers may verify arbitrary strings — needs UX decision for confirmation/context.
- P1 — `cli.js:312` — audit skips all `.env` files globally — needs policy for managed vs committed env files.


---

# Consensus — site-factory
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env:10`, `.env:13`, `sites/*/app/.env.local:5-7`: live Cloudflare/Stripe/webhook secrets are in the project tree. Rotate immediately, remove all real `.env*`, add `.gitignore`, move secrets to 1Password/Vercel/PM2 env.
- `ecosystem.config.js:49`, `admin/server.js:43-46`: production PM2 enables `SF_ADMIN_DEV=1`, making every request auto-admin. Set default to `0`, bind admin to `127.0.0.1`, require real OAuth.
- `admin/server.js:15`: production can boot with hardcoded `SESSION_SECRET`, enabling session forgery if dev bypass is later removed. Refuse boot in production unless `SESSION_SECRET` is set and strong.
- `orchestrator/server.js:15`, `orchestrator/server.js:37`, `orchestrator/server.js:96`, `orchestrator/server.js:130`, `orchestrator/server.js:164`, `orchestrator/server.js:180`: orchestrator has open CORS and no auth on write routes. Add shared bearer auth, restrict CORS, expose only behind admin.
- `orchestrator/server.js:195-204`, `stages/runners.js:240-243`: concurrent `/advance` calls can run the same stage twice, duplicating DNS/Stripe/Vercel work. Use DB transaction/advisory lock and only advance `pending` stages.
- `sites/wholivedthere.com/app/lib/db.ts:30-33`, `db/schema.sql:80-89`: webhook writes `site,email` columns that do not exist, then catches/logs and still returns 200. Align schema/query and make persistence failure return 500/retry.


## Round-1 applied
## Applied
- admin/server.js:15 - removed the hardcoded `SESSION_SECRET` fallback and now fail fast unless a 32+ character secret is provided - prevents booting with a known session signing secret.
- admin/server.js:139 - bound the admin Express server to `127.0.0.1` - keeps the local-only admin surface off public interfaces.
- admin/.env.example:14 - replaced the weak session placeholder with a long random-secret placeholder - documents the required env without adding a real secret.
- sites/wholivedthere.com/app/app/api/webhook/stripe/route.ts:11 - production now returns 500 when Stripe or `STRIPE_WEBHOOK_SECRET` is missing - avoids silently accepting unsigned live webhook events.
- sites/bubbesblock.com/app/app/api/webhook/stripe/route.ts:11 - production now returns 500 when Stripe or `STRIPE_WEBHOOK_SECRET` is missing - avoids silently accepting unsigned live webhook events.
- sites/claimmyaddress.com/app/app/api/webhook/stripe/route.ts:11 - production now returns 500 when Stripe or `STRIPE_WEBHOOK_SECRET` is missing - avoids silently accepting unsigned live webhook events.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
none observed.


## Round-2 applied
## Round 2 Applied
- No safe round-2 code changes applied; the only P0/BROKEN findings target files under `.next/`, which this pass is explicitly forbidden to edit.


## Still deferred for Steve
- P0 - .env:10, .env:13, sites/*/app/.env.local:5-7 - live Cloudflare/Stripe/webhook secrets are in the project tree - rotating/removing real secrets requires Steve-controlled credential rotation; left `.env*` files untouched.
- P0 - ecosystem.config.js:49, admin/server.js:43-46 - PM2 sets `SF_ADMIN_DEV=1`, enabling auto-admin - PM2 ecosystem files are explicitly out of scope; OAuth/dev-bypass policy needs owner confirmation.
- P0 - orchestrator/server.js:15,37,96,130,164,180 - orchestrator has open CORS and unauthenticated write routes - adding shared auth/restricting CORS is a behavioral/API change beyond the safe fix list.
- P0 - orchestrator/server.js:195-204, stages/runners.js:240-243 - concurrent advance calls can duplicate stage work - advisory locks/transaction semantics are not a surgical safe change.
- P0 - sites/wholivedthere.com/app/lib/db.ts:30-33, db/schema.sql:80-89 - webhook persistence schema mismatch - `.sql`/schema and DDL changes are forbidden in this pass.
- P1 - orchestrator/server.js:110-112,144-146, db/schema.sql:16-25 - missing `theme_overrides`/`copy_overrides` schema columns - migration/schema work is forbidden.
- P1 - orchestrator/server.js:24-35,158-160 - async Express routes lack centralized error wrappers - broader route behavior change not included in safe list.
- P1 - stages/runners.js:76-123,203-212 - stub stages advance as done - pipeline semantics need owner decision.
- P1 - critic/server.js:112-148 - `/critique/stub` has no auth/rate limit - auth policy change beyond safe list.
- P1 - scripts/deploy-vercel.sh:40-45 - deploy script pushes every `.env.local` key to Vercel production - production env allowlist needs Steve review to avoid dropping required keys.


---

# Consensus — stayclaim
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env.local:4`: real Google Maps browser key is in the workspace; if unrestricted, cost/abuse risk. Rotate it, remove `.env.local`, enforce HTTP referrer restrictions and quotas.
- `scripts/filmla-cpra-followup.sh:15-16`: hardcoded Basic Auth credential to a Gmail HTTP agent over plain HTTP tailnet URL; repo leak grants email search/send. Rotate credential; load from Keychain/env; require TLS or strict Tailscale ACL.
- `src/app/admin/layout.tsx:45-46`, `src/app/admin/data/page.tsx:167-192`: admin is explicitly unauthenticated and exposes claim/sponsored emails. Put real server-side auth in front of all `/admin/*`, plus nginx deny until ready.
- `src/app/dashboard/page.tsx:11-14`, `src/app/dashboard/page.tsx:21`, `src/app/dashboard/page.tsx:59-63`: `?email=` acts as authentication; anyone who knows an email can view owner listings/pending claims. Replace with magic-link/session auth before exposing.
- `src/app/search/page.tsx:128-130`, `src/app/api/search/route.ts:107-109`: OCR snippets are rendered with `dangerouslySetInnerHTML`; OCR/source text can become stored XSS. Escape source text and render `<mark>` tokens safely.
- `package.json:17`: `next@14.2.15` is below the patched `14.2.25` for CVE-2025-29927 middleware auth bypass; especially dangerous if `/admin` auth is added in middleware only. Upgrade Next or block `x-middleware-subrequest` at the proxy. 


## Round-1 applied
## Applied
- `scripts/filmla-cpra-followup.sh:16` — removed the hardcoded Basic Auth credential and read it from `FILMLA_GEORGE_BASIC_AUTH` — prevents source exposure of the Gmail HTTP agent password.
- `scripts/filmla-cpra-followup.sh:41` — added a fail-fast check when `FILMLA_GEORGE_BASIC_AUTH` is missing — avoids silently calling the agent without required credentials.
- `.env.example:5` — added a placeholder for `FILMLA_GEORGE_BASIC_AUTH` — documents the required secret without copying the real value.
- `src/app/search/page.tsx:42` — added a small snippet renderer that treats only literal `<mark>` tokens as markup — preserves highlights while letting React escape OCR/source text.
- `src/app/search/page.tsx:144` — replaced `dangerouslySetInnerHTML` with React-rendered snippet content — removes the stored DOM injection path flagged for search results.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
`scripts/filmla-cpra-followup.sh:41` — launchd/non-interactive runs will not automatically load `.env.example` or shell profile vars, so the scheduled CPRA follow-up now exits before checking/sending unless the launchd job is separately updated with `FILMLA_GEORGE_BASIC_AUTH` — severity P1


## Round-2 applied
## Round 2 Applied
- `scripts/filmla-cpra-followup.sh:16` — added optional sourcing of `${FILMLA_CPRA_ENV_FILE:-$HOME/.filmla-cpra-followup.env}` before `FILMLA_GEORGE_BASIC_AUTH` is read — fixes re-review "STATUS broken" / P1 finding that launchd/non-interactive runs would otherwise exit before checking or sending.
- `.env.local.example:4` — added `FILMLA_GEORGE_BASIC_AUTH` placeholder — fixes re-review missed call-site: local setup template lacked the required secret.
- `.env.production.example:3` — added `FILMLA_GEORGE_BASIC_AUTH` placeholder — fixes re-review missed call-site: production setup template lacked the required secret.


## Still deferred for Steve
- P0 — `.env.local:4` — real Google Maps browser key exists in the workspace — needs key rotation, referrer restrictions, quotas, and `.env.local` handling outside this safe patch.
- P0 — `scripts/filmla-cpra-followup.sh:15` — Gmail agent still uses a plain HTTP tailnet URL and the removed credential still needs rotation — requires Steve’s secret rotation and network/Tailscale decision.
- P0 — `src/app/admin/layout.tsx:45` — `/admin/*` is explicitly unauthenticated — auth/proxy policy is larger than the allowed safe fixes.
- P0 — `src/app/dashboard/page.tsx:11` — `?email=` acts as authentication — requires product/auth flow changes.
- P0 — `package.json:17` — Next version is below the reviewed patched version — package version bumps are out of scope.
- P1 — `src/app/claim/[slug]/page.tsx:34` — claim form has no submission path — feature behavior change, not in safe list.
- P1 — `src/app/search/page.tsx:44` — server fetch trusts the incoming `Host` header — not in safe list; needs routing/search architecture decision.
- P1 — `src/app/api/search/route.ts:47` — no max query length before DB fanout — validation policy change, not in safe list.
- P1 — `scripts/extract-news-addresses.ts:212` — delete/insert/mark flow lacks transaction/advisory lock — transaction/concurrency change deferred.
- P1 — `src/app/admin/data/page.tsx:217` — filter DB errors are swallowed — admin UX/reporting decision deferred.
- P1 — `package.json:12` — `ingest:airbnb` points to a missing script — command removal/restoration decision deferred.


---

# Consensus — the-ai-factory
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `server.js:67`, `server.js:95`, `server.js:31`: no auth plus `Access-Control-Allow-Origin: *`; any webpage Steve visits can POST to localhost and trigger builds/activation. Fix: require `ADMIN_TOKEN` Bearer auth on all mutating routes and restrict CORS to the viewer origin.
- `server.js:95`, `server.js:104`, `server.js:143`: activation copies LLM-generated files into `~/.claude` with only status gating, and `?force=true` bypasses approval. Fix: remove force from HTTP or require token + explicit local CLI confirmation; show/record a deterministic diff before copy.
- `src/stages/scaffold.js:16`, `src/stages/smoke_test.js:20`: generated subagents may receive `Bash`, `Write`, or `Edit`; smoke tests only check that tools exist, not whether they are safe. Fix: parse frontmatter and deny dangerous tools unless explicitly allowlisted by artifact type/request.
- Secrets leaked in code: none observed; checked-in `.env` is blank (`.env:4`, `.env:10`, `.env:14`).
- SQL injection: none observed; queries use placeholders.
- Obvious dependency CVEs from package files: none observed.


## Round-1 applied
## Applied
- `src/llm.js:10` — replaced the user-specific absolute Claude CLI fallback path with `claude` — removes a hardcoded `/Users/...` path while preserving env override via `CLAUDE_CLI`.
- `.env.example:15` — added `CLAUDE_CLI=claude` placeholder — documents the configurable CLI path without introducing a real secret or machine-local path.


## Still deferred for Steve
- P0 — `server.js:31`, `server.js:67`, `server.js:95` — no auth plus wildcard CORS allows browser-origin localhost POSTs — deferred because adding token auth/CORS policy is outside the safe-fix list and changes operator workflow.
- P0 — `server.js:95`, `server.js:104`, `server.js:143` — activation can copy generated files into `~/.claude` and `?force=true` bypasses approval — deferred because removing force or adding local confirmation/diff changes activation semantics.
- P0 — `src/stages/scaffold.js:16`, `src/stages/smoke_test.js:20` — generated subagents may include dangerous tools and smoke tests only verify presence — deferred because safe remediation requires a frontmatter parser and artifact-specific allowlist design.
- P1 — `server.js:78` — fire-and-forget pipeline can leave runs stuck after a process crash — deferred because a resumable worker/stale-job recovery is a behavioral change beyond the safe list.
- P1 — `src/stages/memory_write.js:32` — read-modify-write append to `MEMORY.md` can race under concurrent activations — deferred because lock/atomic append behavior needs concurrency testing.
- P1 — `src/pipeline.js:52`, `src/llm.js:51` — no concurrency limit around Ollama/Claude CLI — deferred because queue sizing and backpressure policy need Steve’s decision.
- P1 — `README.md:17`, `package.json:8` — docs say `npm run start` boots orchestrator and viewer, but it only starts the orchestrator — deferred because docs/public-facing command behavior changes were not in the safe-fix list.
- P1 — `PLAN.md:106` — completed items are duplicated as open TODOs — deferred because plan/status cleanup is not in the safe-fix list.
- P1 — `package.json:15`, `.env.example:4`, `src/llm.js:5` — Anthropic SDK/API key remain despite “No Anthropic API” — deferred because dependency/env removal is not a safe autonomous fix and may require package-lock decisions.


---

# Consensus — trademarks-copyright
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `package.json:18`, `src/middleware.ts:14`: Next `15.0.3` is vulnerable to middleware auth bypass; admin auth relies on middleware. Upgrade to `15.2.3+` or block `x-middleware-subrequest` at the edge. citeturn0search4
- `src/app/api/drops/send/route.ts:15`, `src/app/api/drops/compose/route.ts:8`, `src/app/api/score/route.ts:9`, `src/app/api/brands/hunt/route.ts:12`: public mutation routes can send newsletters, compose drops, rescore DB, and run crawler/LLM jobs. Put all operational routes behind server-side auth, not only middleware path matching.
- `src/app/api/drops/drop-items/[id]/route.ts:8`: public PATCH edits newsletter content/CTA URLs. Move under `/api/drops/admin/*` or enforce auth inside the handler.
- `src/app/api/drops/checkout/route.ts:23`: if Stripe is unset, any caller can set any subscriber email to paid tier at lines `24-30`. Remove dev fallback outside `NODE_ENV !== "production"` and require subscriber token/session.
- `.env.local:30`, `.env.local:47-48`: plaintext admin token and Basic auth credential, including raw password comment, sit in project dir. Rotate both; store outside repo/workspace backups.
- `src/app/api/drops/cron/route.ts:15`: cron route is open if `CRON_TOKEN` is unset; `.env.local:25` uses `change-me-before-public`. Fail closed when missing and rotate token.


## Round-1 applied
## Applied
- src/app/api/drops/cron/route.ts:14 — require `CRON_TOKEN` to be configured before accepting cron requests — prevents the reviewed fail-open cron path when the env secret is missing.
- src/app/api/drops/checkout/route.ts:7 — return 400 when the parsed JSON body is `null` or non-object — prevents malformed JSON values from becoming handler errors.
- src/app/api/drops/send/route.ts:17 — return 400 when the parsed JSON body is `null` or non-object — prevents malformed JSON values from becoming handler errors.
- src/app/api/drops/compose/route.ts:10 — return 400 when the parsed JSON body is `null` or non-object — prevents malformed JSON values from becoming handler errors.
- src/app/api/brands/hunt/route.ts:14 — return 400 when the parsed JSON body is `null` or non-object — prevents malformed JSON values from becoming handler errors.
- src/app/api/drops/drop-items/[id]/route.ts:17 — return 400 when the parsed JSON body is `null` or non-object — prevents malformed JSON values from becoming handler errors.
- src/app/api/drops/admin/subscribers/[id]/route.ts:13 — return 400 when the parsed JSON body is `null` or non-object — prevents malformed JSON values from becoming handler errors.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
none observed.


## Round-2 applied
## Round 2 Applied
- src/app/api/swot/route.ts:10 — added a non-null object/array guard before reading `body.itemId` and `body.force` — fixes CODEX_REREVIEW.md missed call-site for JSON `null` reaching `body.itemId`.
- src/app/api/drops/signup/route.ts:10 — added a non-null object/array guard before reading `body.email`, `body.name`, and `body.ref` — fixes CODEX_REREVIEW.md missed call-site for JSON `null` reaching `body.email`.


## Still deferred for Steve
- P0 — package.json:18, src/middleware.ts:14 — vulnerable Next middleware auth bypass — deferred because package upgrades/edge blocking were outside the safe fix list.
- P0 — src/app/api/drops/send/route.ts:15, src/app/api/drops/compose/route.ts:8, src/app/api/score/route.ts:9, src/app/api/brands/hunt/route.ts:12 — public operational mutation routes — deferred because route auth design is broader than a surgical safe fix.
- P0 — src/app/api/drops/drop-items/[id]/route.ts:8 — public PATCH can edit newsletter content — deferred because moving/enforcing admin auth changes route structure and access policy.
- P0 — src/app/api/drops/checkout/route.ts:23 — Stripe-disabled checkout can directly upgrade subscribers — deferred because billing fallback/session policy needs owner decision.
- P0 — .env.local:30, .env.local:47-48 — plaintext admin/basic credentials — deferred because real secret rotation must be done by Steve; `.env.local` was left untouched.
- P0 — .env.local:25 — placeholder cron token — deferred because rotation of the real token must be done by Steve.
- P1 — src/app/api/drops/send/route.ts:43, src/app/api/drops/send/route.ts:85, src/lib/drops.ts:381 — transient email failures mark deliveries bounced — deferred because retry semantics/data handling are not in the safe list.
- P1 — src/lib/drops.ts:197, src/lib/drops.ts:233 — compose race without transaction/lock — deferred because transaction/locking behavior is beyond the approved safe fixes.
- P1 — src/app/api/drops/admin/subscribers/[id]/route.ts:18 — subscriber values are not enum/range validated — deferred because validation rules need owner-approved state constraints.
- P1 — src/lib/brandHunter.ts:283, src/app/api/brands/hunt/route.ts:18 — caller controls seeds/cap — deferred because auth/resource policy changes are broader than the safe body guard applied.
- P1 — src/lib/email.ts:96 — file email fallback writes recipient HTML under `/tmp/drops` — deferred because production email-backend policy needs Steve.


---

# Consensus — VictoryStays
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- [server.js](/Users/stevestudio2/Projects/VictoryStays/server.js:51): `express.static(__dirname)` publishes the whole repo, including `data/victorystays.db`, source, lockfiles, generated datasets, and future `data/claims.jsonl`. Fix: serve a dedicated `public/` dir and explicitly block `/data`, scripts, DB/WAL/SHM, logs.
- [api-claim.js](/Users/stevestudio2/Projects/VictoryStays/api-claim.js:81): `GET /api/claim?admin=1` returns every claim, PII, and verification codes with no auth. Fix: require operator auth/session or remove public route.
- [api-claim.js](/Users/stevestudio2/Projects/VictoryStays/api-claim.js:103): unauthenticated `PATCH /api/claim` can mark any claim verified/rejected; code check is optional. Fix: authenticate operator actions and require code for verification.
- [api-claim.js](/Users/stevestudio2/Projects/VictoryStays/api-claim.js:72), [home.html](/Users/stevestudio2/Projects/VictoryStays/home.html:434): claim submission returns the postcard verification code to the claimant, defeating the mail-based proof. Fix: never return the code; only store/print/mail it.
- [api-claim.js](/Users/stevestudio2/Projects/VictoryStays/api-claim.js:27): unbounded JSON body plus sync append lets anyone fill disk with claims. Fix: `express.json({limit})`, rate-limit/IP throttle, validate field lengths.


## Round-1 applied
## Applied
- templates/city.html:454 — added a small `escapeHTML` helper and used it for user-entered street text, API parcel fields, assessor links, and error messages before rendering with `innerHTML` — prevents DOM injection while preserving the existing markup.
- search.html:121 — added the same local `escapeHTML` helper and escaped query text, parcel/assessment fields, permit rows, assessor links, and error messages before `innerHTML`/`insertAdjacentHTML` rendering — fixes the flagged client-side XSS risk without changing customer-facing copy.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
none observed


## Round-2 applied
## Round 2 Applied
- beverly-hills.html:21958 — added local `escapeHTML` helper and escaped street text, parcel fields, assessor link URL, and lookup error before `innerHTML` rendering — fixes CODEX_REREVIEW "Missed call-sites" at beverly-hills.html:21968 and beverly-hills.html:21997, plus the template-generation "STATUS concern".
- burbank.html:15058 — added local `escapeHTML` helper and escaped street text, parcel fields, assessor link URL, and lookup error before `innerHTML` rendering — fixes CODEX_REREVIEW "Missed call-sites" at burbank.html:15068 and burbank.html:15097, plus the template-generation "STATUS concern".
- west-hollywood.html:28490 — added local `escapeHTML` helper and escaped street text, parcel fields, assessor link URL, and lookup error before `innerHTML` rendering — fixes CODEX_REREVIEW "Missed call-sites" at west-hollywood.html:28500 and west-hollywood.html:28529, plus the template-generation "STATUS concern".


## Still deferred for Steve
- P0 — server.js:51 — `express.static(__dirname)` publishes the repo tree — deferred because changing the public asset root/routing is broader than the approved safe fixes and could break live paths.
- P0 — api-claim.js:81 — admin claim firehose exposes claims/PII/codes without auth — deferred because it needs an operator auth/session decision.
- P0 — api-claim.js:103 — unauthenticated claim status updates and optional verification code — deferred because it needs auth and verification workflow decisions.
- P0 — api-claim.js:72 — claim submission returns the postcard verification code — deferred because removing it changes the operator/claim API contract.
- P0 — api-claim.js:27 — unbounded JSON body and synchronous append can fill disk/block the process — deferred because rate limits, field limits, and storage behavior need product/runtime choices.
- P1 — api-claim.js:17 — admin/list lookups synchronously parse the full JSONL file — deferred because the recommended SQLite move touches persistence design.
- P1 — api-claim.js:120 — PATCH rewrites the full claims file without locking/atomic rename — deferred because concurrency-safe persistence changes need more validation.
- P1 — api-property.js:75 — ArcGIS `LIKE` query allows broad wildcard-heavy searches — deferred because input policy changes were outside the approved safe list.
- P1 — api-build-status.js:15 — status API opens the production SQLite DB read/write and changes WAL mode — deferred because DB access-mode changes were outside the approved safe list.
- P1 — scripts/enrich-listings-worker.js:95 — transient API failures permanently mark listings failed — deferred because retry state/backoff changes affect worker behavior and likely persistence.


---

# Consensus — visual-factory
Generated 2026-04-30T23:48:02-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `server.js:223`, `server.js:257`, `server.js:279`, `server.js:326`, `server.js:362`: no auth/CSRF on create, retry, delete, activate, IG post. Any local webpage/process reaching `127.0.0.1:9892` can trigger destructive POSTs. Fix with required admin token plus Origin/CSRF checks.
- `src/stages/compose.js:34-44`, `src/stages/render.js:13`: untrusted LLM HTML is written then opened as `file://` with JS/network enabled. Prompt injection can embed `file://`/remote URLs and leak local data via screenshots or callbacks. Fix by sanitizing, disabling JS, using request interception/CSP, and blocking all `file:`/external loads.
- `server.js:263-271`: retry accepts `running`, deletes rows/output, then enqueues another pipeline while the original continues. Failure mode: corrupted events/artifacts and competing writes to the same run dir. Fix by refusing running runs or implementing cancellation/locking.
- `src/stages/intake.js:35-36`, `src/stages/render.js:11-16`: LLM-controlled dimensions are not clamped. A bad spec can request huge viewport/screenshot and exhaust memory. Fix with min/max bounds before DB/render.


## Round-1 applied
## Applied
- src/llm.js:10 — changed the Claude CLI fallback from a user-specific absolute path to `claude` from `PATH` — removes a hardcoded `/Users/...` path from source.
- server.js:107 — added the same `CLAUDE_CLI || 'claude'` default for deep health checks — keeps server and LLM adapter behavior aligned.
- server.js:109 — added a small executable lookup helper for absolute paths, relative paths, and `PATH` commands — lets `/health/deep` validate `claude` without a hardcoded home directory.
- server.js:204 — changed the Claude CLI health probe to use the new lookup helper — avoids checking Steve's local absolute path.
- .env.example:14 — changed `CLAUDE_CLI` to `claude` — documents the portable default without copying a real local path.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
server.js:225 — `fs.access(command)` defaults to existence only, so a non-executable absolute/relative `CLAUDE_CLI` passes `/health/deep` while `spawn()` later fails with EACCES — severity P2  
server.js:231 — PATH lookup also uses existence only, so a non-executable file named `claude` earlier in PATH can make health green while critic execution fails — severity P2  


## Round-2 applied
## Round 2 Applied
- server.js:5 — imported `fsConstants` from `node:fs` — needed so executable checks can use `X_OK` instead of existence-only access (CODEX_REREVIEW.md “New issues introduced” server.js:225/server.js:231 and “Recommendations”).
- server.js:224 — updated `assertExecutable()` to reject directories and require execute permission for absolute/relative `CLAUDE_CLI` paths and `PATH` candidates — fixes `/health/deep` false positives where non-executable files could report `claude_cli: ok` while `spawn()` would later fail (CODEX_REREVIEW.md “STATUS concern” and P2 new issues at server.js:225/server.js:231).


## Still deferred for Steve
- P0 — server.js:223 — create/retry/delete/activate/IG POST routes lack auth/CSRF — deferred because token, Origin, and CSRF policy are outside the safe fix list.
- P0 — src/stages/compose.js:34, src/stages/render.js:21 — LLM HTML is written and opened as `file://` with JS/network behavior unresolved — deferred because sanitization/CSP/request-blocking choices need product/security decisions.
- P0 — server.js:259 — retry/cancellation semantics for live runs still need an owner decision, especially `force=true` behavior — deferred because cancellation/locking is outside the safe fix list.
- P0 — src/stages/intake.js:35, src/stages/render.js:19 — generated dimensions are not clamped before rendering — deferred because bounds policy is not in the safe fix list.
- P1 — server.js:333 — `/runs/:id/png` sends DB paths without sandbox validation — deferred because artifact path trust policy is outside the requested safe fixes.
- P1 — server.js:405 — activation validates `png_path` but not `html_path` before copying — deferred for the same sandbox policy reason.
- P1 — src/stages/vision_check.js:40, src/stages/critic.js:21 — failed vision can still approve at quality 75 — deferred because changing approval policy affects generated asset workflow.
- P1 — src/pipeline.js:195 — crash logging can use stale `run.current_stage` — deferred because it is not in safe fix items 1-7.
- P1 — server.js:413, server.js:442 — activation copy and manifest update are not locked/atomic — deferred because concurrency design is outside the safe fix list.
- P1 — src/stages/compose.js:16, src/stages/render.js:21 — Google Fonts plus `networkidle` can stall offline runs — deferred because asset bundling/request policy is outside the safe fix list.
- P1 — package.json:10 — `npm run run` points to a missing script — deferred because script changes are not in safe fix items 1-7.


---

# Consensus — wholivedthere
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `.env:2-5`, `scripts/deploy-direct.sh:21-22`, `scripts/deploy-pastdoor.sh:28-29`: committed Google/DPLA/GoDaddy API secrets; DNS/domain takeover and API billing exposure. Revoke/rotate now, remove defaults, keep only `.env.example`.
- `scripts/deploy-direct.sh:60`: deploy path drops `pastdoor` database every run; one mistaken rerun destroys production data. Replace with migration/restore into temp DB plus explicit manual confirmation.
- `mcp-la-records/src/arcgis.mjs:83-93`, `mcp-la-records/src/arcgis.mjs:110`: user input is interpolated into ArcGIS SQL-like `where`; quotes/wildcards can alter query semantics. Validate APN with strict regex, escape `%/_/'`, and reject unsupported address chars.
- `mcp-la-records/src/arcgis.mjs:97`: `limit` is unvalidated and passed as `resultRecordCount`; callers can request huge result sets or invalid counts. Clamp to small integer range, e.g. `1..25`.
- `scripts/schema.sql:58`: generated column uses `now()`, which Postgres rejects because generated expressions must be immutable. Replace with a view or query predicate for stale rows.


## Round-1 applied
## Applied
- `scripts/deploy-direct.sh:21` — removed hardcoded GoDaddy credential fallbacks and now reads `GODADDY_API_KEY` / `GODADDY_API_SECRET` from the environment only — prevents exposed source defaults from being used.
- `scripts/deploy-direct.sh:24` — added a fail-fast check when GoDaddy credentials are missing — avoids running DNS updates with empty credentials.
- `scripts/deploy-pastdoor.sh:28` — removed hardcoded GoDaddy credential fallbacks and now reads `GODADDY_API_KEY` / `GODADDY_API_SECRET` from the environment only — prevents exposed source defaults from being used.
- `scripts/deploy-pastdoor.sh:32` — added a fail-fast check when GoDaddy credentials are missing — avoids running DNS/registrar steps with empty credentials.
- `.env.example:3` — added placeholder-only entries for DPLA, GoDaddy, and Cloudflare variables — documents required env vars without copying real secrets.


## Codex re-review verdict: SHIP IT

## New issues introduced
none observed


## Still deferred for Steve
- P0 — `.env:2` — real Google/DPLA/GoDaddy secrets are present according to the review — left `.env` untouched and credential rotation needs account access.
- P0 — `scripts/deploy-direct.sh:65` — deploy script drops the `pastdoor` database — destructive database workflow is outside the safe fix list and needs an owner-approved deployment design.
- P0 — `mcp-la-records/src/arcgis.mjs:83` — ArcGIS `where` query interpolates user input — query semantics and validation rules need product-aware handling.
- P0 — `mcp-la-records/src/arcgis.mjs:97` — unvalidated `limit` reaches ArcGIS result count — API behavior change was not in the approved safe list.
- P0 — `scripts/schema.sql:58` — generated column uses `now()` — SQL/schema changes are explicitly out of scope.
- P1 — `mcp-la-records/src/arcgis.mjs:39` — fetch has no timeout — network behavior change was not in the approved safe list.
- P1 — `mcp-la-records/src/arcgis.mjs:99` — address lookup swallows upstream failures — API error semantics change was not in the approved safe list.
- P1 — `mcp-la-records/src/index.mjs:69` — MCP handler lacks runtime `args` validation — validation contract change was not in the approved safe list.
- P1 — `scripts/deploy-pastdoor.sh:60` — rsync does not exclude `.env*` — deploy behavior change was not one of the allowed safe fix categories.
- P1 — `scripts/smoke-test.sh:65` — smoke test accepts API `404` responses — test expectation change was not in the approved safe list.
- P1 — `PLAN.md:1` / `INTEGRATION.md:1` — docs disagree on architecture — documentation rewrite was not requested for safe fixes.


---

# Consensus — yolo-agent
Generated 2026-04-30T23:48:03-07:00. Source: codex review/fix/rereview/round-2 artifacts (round-3 was quota-blocked).

## Codex pass-1 review (top risks)
## Top Risks (P0 — fix this week)
- `server.js:17-18`, `ledger-app/src/lib/xfetch.ts:18`, `dashboard/task-ledger.html:279`: hardcoded `admin:DWSecure2024!` shipped in server and browser code. Anyone who sees the UI/source can control the agent. Fix: env-only secret, rotate password, remove client-side auth embedding.
- `server.js:614-625` + `server.js:226-239`: `/api/run` accepts arbitrary prompt, auto-starts, and launches Claude with `Bash/Write/Edit` in `/Users/stevestudio2/Projects`. With leaked auth this is remote code execution over the whole projects tree. Fix: bind to VPN/localhost, remove dangerous tools by default, require manual approval for write/bash tasks.
- `create-missing-collections.js:15`, `scripts/collection-descriptions.js:16`, `tasks/done/AG_dw-exclusive-page-update.md:9`, `tasks/done/14_shopify-image-audit.md:5`: live-looking Slack webhook, Gemini key, Shopify tokens, and Postgres credentials are committed in code/task artifacts. Fix: revoke/rotate all exposed secrets and purge/redact task archive.
- `server.js:1038-1043`: service binds `0.0.0.0` and prints public IP dashboard URL while auth is static/basic. Failure mode is internet-exposed agent control. Fix: bind `127.0.0.1` behind SSH/VPN or enforce firewall plus fresh credentials.


## Round-1 applied
## Applied
- server.js:18 — removed hardcoded Basic Auth defaults and fail fast unless `YOLO_AUTH_USER`/`YOLO_AUTH_PASS` are set — prevents fallback to shipped credentials.
- server.js:605 — added JSON body guards on task/mode/cooldown POST routes — malformed requests now return 400 instead of destructuring failures.
- server.js:1051 — changed listener from public bind to `127.0.0.1` and removed the public dashboard URL — local-only tool should not expose the agent port.
- ledger-app/src/lib/xfetch.ts:18, dashboard/task-ledger.html:584, ledger-dist/assets/index-BwgB1DeC.js:149 — removed embedded `admin` password headers and use same-origin credentials — avoids shipping agent credentials in browser code.
- create-missing-collections.js:15, scripts/collection-descriptions.js:17 — moved Slack/Gemini/local dashboard auth to env vars with fail-fast checks — removes live-looking secrets from source.
- scripts/body-html-cleanup.js:72 — escaped generated product title/vendor HTML and changed logs/env loading to project-relative paths — prevents stored HTML injection and nonportable `/root` paths.
- scripts/collection-image-refresh.js:91, orphan-audit.js:188 — replaced `/root` log/report paths with paths derived from `__dirname` — keeps generated files inside this project.
- .env.example:1 — added placeholder env keys only — documents required config without adding real secrets.


## Codex re-review verdict: FIX BEFORE SHIP

## New issues introduced
create-missing-collections.js:3 — new `.env` loading depends on `dotenv`, but `package.json:9` dependencies do not include `dotenv`; the catch hides the import failure, so copying `.env.example` will not satisfy the new fail-fast env checks — severity P1  
scripts/collection-descriptions.js:3 — same missing `dotenv` dependency makes documented `.env` config ineffective for this script’s new required env vars — severity P1  
server.js:1051 — bind changed to loopback, but remote setup still advertises host-IP access at `setup-yolo-remote.sh:196`; a normal remote install will appear live but be unreachable except locally/through a tunnel — severity P2


## Round-2 applied
## Round 2 Applied
- server.js:15 — added a dependency-free `.env` loader before auth env vars are read — fixes the `server.js`/`.env.example` concern that documented credentials must actually satisfy fail-fast startup without adding `dotenv`.
- create-missing-collections.js:5 — replaced the hidden `require('dotenv')` dependency with a local `.env` parser — fixes the P1 rereview finding that missing `dotenv` made `.env.example` ineffective.
- scripts/collection-descriptions.js:5 — replaced the hidden `require('dotenv')` dependency with a local `.env` parser — fixes the same P1 rereview finding for this script's required env vars.
- setup-yolo-remote.sh:196 — changed the setup output from a host-IP dashboard URL to `localhost` plus an SSH tunnel command — fixes the missed call-site where remote setup still assumed public dashboard access after loopback binding.


## Still deferred for Steve
- P0 — server.js:226 — Claude still runs with Bash/Write/Edit over `/Users/stevestudio2/Projects` — permission/workflow redesign is not a safe surgical fix.
- P0 — server.js:614 — `/api/run` still queues arbitrary prompts and auto-starts — changing approval semantics needs owner decision.
- P0 — tasks/done/* — committed task artifacts still contain exposed Slack/Gemini/DB/auth values — requires credential rotation and archive purge/redaction by Steve.
- P0 — promote-and-parallel.sh:7, setup-yolo-remote.sh:101 — old auth remains in operational scripts — changing deployment/CLI auth patterns was outside the cited safe source/browser fix.
- P1 — server.js:441 — Basic Auth parser/timing-safe compare unchanged — not in the safe fix list.
- P1 — server.js:486 — `/api/live-log` can read recent `/tmp/*.log` tails — behavior change needs log ownership design.
- P1 — server.js:1058 — auto-retry metadata remains in-memory — persistence changes are not safe without testing.
- P1 — promote-and-parallel.sh:45 — parallel workers still bypass scheduler guardrails — scheduler redesign deferred.
- P1 — server.js:491 — synchronous dashboard API work remains — async/cache refactor deferred.


---