The first review called this "a capable, fast-moving product on shaky operational and security foundations" — 3 criticals, ~18 highs, decorative CI, committed credentials. A session of focused work closed the two most dangerous criticals and turned CI into a real gate. What remains is narrower and more structural: authorization breadth, a god-file, and a repo that still treats prod as a scratchpad.
uuid→text; RLS de-cast.test_no_uuid_customer_id.py) now fails the build if the pattern returns — the rule is enforced, not just documented.digital_twin is a separate instance (postgres-digital-twin), not a schema in postgres-prod; recipe documented.FruitScout/FarmAgent-Context created; the rule codified in AGENTS.md (RULE #2), CLAUDE.md, context.md, habits.md.Each maps to a finding from the first review. Statuses are evidence-based from a fresh scan of main; two are marked verify where the scan saw the surface but didn't confirm the fix end-to-end.
FA2_JWT_SECRET hardcoded in tracked scratch scripts — anyone could forge tokens. Now: zero hardcoded secrets in services/api; loaded from Secret Manager; a blocking gitleaks scan in CI; historical secrets rotated/neutralized.customer_id was a UUID while dt_* stores a text name — the policy comparison could never match, so isolation rested entirely on per-query WHERE. Now: customer_id is text everywhere, RLS compares text=text, and a guard test prevents regression.customer_id from the client form/body, never compared to the JWT. Now: the resolver sweep moved routers (incl. campo_plan) to a JWT-derived customer_id — a client can no longer pass customer_id=Sauza. Verify the form parameter is fully removed from every campo_plan path./api/internal/* exempt from auth — could mint API keys / list users with no credentials. Now: /api/internal is still an open middleware prefix, and all GET requests bypass APIKeyMiddleware entirely — read-endpoint protection depends on per-route deps being applied consistently (un-audited). Still a launch-blocker.CANOPY_CALLBACK_SECRET unset, the HMAC check is skipped; endpoint fetches a client-supplied gcs_uri. Now: /api/review/canopy/callback remains an open prefix; fail-closed behavior not confirmed. Make it fail-closed.upgrade-labor-tier self-serve; switch-customer issued tokens for any customer. Now: entitlement is enforced server-side only in the Labor router; POST /api/auth/upgrade still mints a management token. Billing is now real (Stripe), but gating breadth is the pre-launch item.| Dimension | Jun 27 | Now | What moved |
|---|---|---|---|
| Security & tenancy | Critical gaps | Improved · gaps remain | Secrets remediated, tenancy bug closed; unauth-internal & authz-breadth open. |
| Data model & ETL | Integrity risks | Clarified | Hard Wall confirmed a separate instance; 038 writes now atomic. Staging-delete invariant still unenforced. |
| Backend architecture | Workable, tangled | Workable, tangled | Dead apps/api/ deleted; review.py god-file (~2,400 LOC, no tests) untouched. |
| Frontend & product | Rich, needs refactor | Rich, needs refactor | Unchanged — 3 parallel OpenLayers maps still un-abstracted; 3 e2e specs. |
| Platform & CI/CD | Fragile / decorative | Now gates (partly) | CI gates ruff + unit tests + blocking secret-scan + gated prod deploy. Type/tsc/eslint still || true. |
Server-side entitlement only in Labor; everything behind FeatureGate is UI-only and bypassable by direct API call. /api/internal + GET-bypass need an audit. The #1 launch-blocker.
review.py god-file~2,400 lines conflating chat, reconciliation, promotion, geometry, jobs — zero tests. The riskiest file to change. Split & cover.
Drop the || true on mypy / tsc / eslint so type and lint failures actually fail the build. The gate exists; it's half-open.
33 tracked scratch_*/debug_*/.dump files (+~1 MB stray data), none gitignored. Move investigation scripts out; gitignore the rest.
gis, review, vlm_agent, campo_plan have no tests; auth has no unit test. Onboarding is well-covered — extend that discipline.
Now the inverse problem: CLAUDE.md understates reality (claims no CI & that apps/api exists — both false). Re-sync it to the improved state.
The dangerous, exploitable-today items that dominated the first review are mostly handled — committed credentials, the tenancy-comparison bug, and the dead-tree/CI-theatre problems are gone or much improved. The codebase is meaningfully safer than it was. What's left is narrower but real: authorization is enforced inconsistently outside Labor, the review.py monolith is an untested liability, and the repo still ships investigation scripts beside product code. None of these is a "stop everything" fire the way a committed signing secret was — they're a focused, finishable punch-list standing between "safe internally" and "safe to sell."