Backfill sparse report sections, add enrichment section refresh, and bootstrap first-admin

Reports: the LLM reliably used company_enrichment for prose fields but
inconsistently populated the parallel Finding-list/string-list fields from
the same evidence, even with progressively more explicit prompting. Add a
code-level backfill (products, recent developments, financial signals,
strategic initiatives, regulatory signals, risks/opportunities mirrored
from SWOT, unknowns, monitoring recommendations) that only ever fills in
what the model left empty, never overwrites what it produced.

Enrichment tab: reorder sections (Products/Recent updates before
Customers/Competitors) and add a per-section "Refresh" button that
re-fetches just one of NinjaPear's six independent per-company endpoints
when it came back empty - confirmed live that a data-coverage gap (e.g.
Amazon returning no products) is real provider behavior, not a bug.

Auth: the first account registered on a deployment with zero existing
admins is now auto-promoted to admin, closing the chicken-and-egg gap
where the only path to admin access was direct DB access. Self-heals if
the last admin ever deletes their account.

Also bumps nginx's proxy_read_timeout for api.ciagent.org to cover the
enrichment refresh's synchronous funding-endpoint call (up to 5 minutes
per NinjaPear's docs).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-06 17:41:02 -04:00
co-authored by Claude Sonnet 5
parent 1be3e53584
commit 18305b545c
18 changed files with 1087 additions and 53 deletions
+25
View File
@@ -48,6 +48,31 @@ def _setup_database():
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# auth_service.register() auto-promotes the first-ever registration
# to admin (see its docstring) so a fresh deployment always has a
# bootstrap path to admin access. Without this seed row, whichever
# test happens to register a "plain" user first in this shared
# session-wide DB would non-deterministically become an admin,
# breaking every non-admin-should-get-403 test depending on
# execution order. Seeding one admin up front keeps that dedicated
# to test_first_registered_user_becomes_admin (which starts from a
# genuinely empty users table), while every other test's "plain"
# registration behaves exactly as before.
from app.models.user import User
async with get_sessionmaker()() as session:
session.add(
User(
email="[email protected]",
password_hash=None,
display_name="Seed Admin",
timezone="UTC",
is_admin=True,
email_verified=True,
)
)
await session.commit()
asyncio.run(_create())
yield