Phase 1: investor↔contacts (member_of), system status, thesis seed v1
- entity_resolution: emit member_of relationship edges (contact -> investor), so one investor entity owns many contacts (institution) and a HNWI is the N=1 case; crm_tools.get_investor_contacts + get_entity contacts/member_of; MCP tool. - seed_synthetic: multi-contact institutions to exercise it (Harbor & Vine = 5). - server.py: GET /api/system/status (index/entity/thesis/activity health) for an in-app status view (no shell needed to verify the index). - docs/thesis-seed-v1.md: grounded v1 thesis (throughline, 6 pillars, objections, per-segment angles, voice) drawn from Ten31's newsletter/site/essays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1728,6 +1728,8 @@ class CRMHandler(BaseHTTPRequestHandler):
|
||||
return self.handle_get_fundraising_activity(user, params)
|
||||
if path == '/api/security/status':
|
||||
return self.handle_security_status(user)
|
||||
if path == '/api/system/status':
|
||||
return self.handle_system_status(user)
|
||||
|
||||
# Users
|
||||
if path == '/api/users':
|
||||
@@ -3428,6 +3430,41 @@ class CRMHandler(BaseHTTPRequestHandler):
|
||||
conn.close()
|
||||
return self.send_json({"message": "Tag deleted"})
|
||||
|
||||
def handle_system_status(self, user):
|
||||
"""System / search-index health for the in-app status view (DB-derived)."""
|
||||
conn = get_db()
|
||||
out = {}
|
||||
try:
|
||||
live = "deleted_at IS NULL"
|
||||
out['canonical_entities'] = {
|
||||
'lp': conn.execute(f"SELECT COUNT(*) FROM canonical_entities WHERE entity_kind='lp' AND {live}").fetchone()[0],
|
||||
'organization': conn.execute(f"SELECT COUNT(*) FROM canonical_entities WHERE entity_kind='organization' AND {live}").fetchone()[0],
|
||||
'person': conn.execute(f"SELECT COUNT(*) FROM canonical_entities WHERE entity_kind='person' AND {live}").fetchone()[0],
|
||||
}
|
||||
out['entity_links'] = conn.execute("SELECT COUNT(*) FROM entity_links").fetchone()[0]
|
||||
except Exception:
|
||||
out['canonical_entities'] = None
|
||||
try:
|
||||
r = conn.execute("SELECT ts, payload FROM interaction_log WHERE action='ingest.sync' ORDER BY ts DESC LIMIT 1").fetchone()
|
||||
out['last_index_sync'] = ({'ts': r['ts'], **json.loads(r['payload'] or '{}')} if r else None)
|
||||
except Exception:
|
||||
out['last_index_sync'] = None
|
||||
try:
|
||||
out['thesis'] = {
|
||||
'lines': conn.execute("SELECT COUNT(*) FROM thesis_lines WHERE deleted_at IS NULL").fetchone()[0],
|
||||
'canonical_versions': conn.execute("SELECT COUNT(*) FROM thesis_versions WHERE status='canonical'").fetchone()[0],
|
||||
'in_review': conn.execute("SELECT COUNT(*) FROM thesis_versions WHERE status='in_review'").fetchone()[0],
|
||||
}
|
||||
except Exception:
|
||||
out['thesis'] = None
|
||||
try:
|
||||
out['recent_activity'] = [dict(r) for r in conn.execute(
|
||||
"SELECT ts, actor_type, actor_id, action FROM interaction_log ORDER BY ts DESC LIMIT 12")]
|
||||
except Exception:
|
||||
out['recent_activity'] = []
|
||||
conn.close()
|
||||
self.send_json({"data": out})
|
||||
|
||||
# ─── Architect thesis (Phase 1) ───
|
||||
def handle_list_thesis_lines(self, user):
|
||||
if thesis_review is None:
|
||||
|
||||
Reference in New Issue
Block a user