Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions apps/qdb-advisory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# QDB Advisory v0

Code-first founder advisory execution loop derived from the Sophie/QDB case.

## Product boundary

This is not a mentor AI, CRM, autonomous advisor, or portfolio-intelligence product.

Closed loop:

`interaction -> evidence -> state change -> decision -> commitment/experiment -> follow-up -> outcome -> next-session brief`

AI proposes semantic structure. Code controls state. Evidence controls truth.

## v0 stack

- Next.js + TypeScript UI/API
- Vercel Workflows for durable orchestration, retries, sleeps, and human/event waits
- Supabase/Postgres as the only durable product source of truth
- Structured LLM extraction behind a strict schema
- Git branches/PRs + preview deployments

No vector DB. No RAG. No autonomous tool calling. No portfolio clustering in v0.

## v0 screens

1. Founder dashboard: current state, commitments, experiments, blockers, trajectory.
2. Session record: source interaction, evidence, decisions, commitments, experiments.
3. Advisor brief: what changed, what did not happen, unresolved commitments, state changes, and 3-5 questions requiring advisor judgment.

Portfolio intelligence is explicitly deferred until repeated real use.

## Runtime flows

### Interaction processing

`POST /api/interactions -> persist immutable interaction -> start processInteraction(interactionId)`

Workflow steps:

1. Load immutable interaction.
2. Extract bounded structured objects.
3. Reject unsupported/low-confidence objects.
4. Persist accepted observations/assumptions/decisions/commitments/experiments transactionally.
5. Rebuild/propose founder state from accepted evidence.
6. Persist a new state version.
7. Start a commitment lifecycle workflow for each new commitment with a due date.

### Commitment lifecycle

`commitment created -> durable wait -> reminder if still open -> due-date check -> founder response -> outcome evidence -> interaction workflow again`

Completion is never inferred. An open commitment whose due date has passed is displayed as overdue; its canonical status remains open until explicit evidence changes it.

### Next-session brief

v0 generates the brief on demand from immutable history + current state. Calendar-triggered generation is added only after real workflow access is earned.

## Non-negotiable invariants

- Raw interactions are immutable.
- Every extracted observation links to an interaction and a supporting source excerpt.
- The LLM cannot directly update canonical state.
- Recommendations do not become commitments without explicit acceptance.
- Missing deadlines remain null.
- Completion is never inferred.
- Founder state is disposable and reconstructable from event history.
- Duplicate interaction ingestion is idempotent.
- Cross-founder writes are impossible by construction.

## Demo dataset

Exactly one fictional founder and three sessions. The third session must prove the chain from prior commitment -> actual execution -> evidence -> state transition -> advisor brief.

## Done when

A raw session passes end-to-end with no manual database edits:

1. Interaction persisted.
2. Evidence extracted.
3. Decisions captured.
4. Explicit commitments created.
5. Previous state retrieved.
6. Evidence-supported state transition proposed.
7. Current state version updated.
8. Due commitments are visible.
9. Founder update reconciles against the prior commitment.
10. Outcome becomes new evidence.
11. State updates again.
12. Next-session brief reflects the whole chain.

Quality floor before external use:

- commitments >95% precision
- decisions >90% precision
- metrics >95% precision
- observations >90% precision
- hallucinated critical objects = 0
114 changes: 114 additions & 0 deletions apps/qdb-advisory/db/migrations/001_core.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
create extension if not exists pgcrypto;

create table if not exists founders (
id uuid primary key default gen_random_uuid(),
name text not null,
company_name text,
stage text,
advisor_id uuid,
created_at timestamptz not null default now()
);

create table if not exists interactions (
id uuid primary key default gen_random_uuid(),
founder_id uuid not null references founders(id) on delete cascade,
occurred_at timestamptz not null,
interaction_type text not null check (interaction_type in ('advisory_session','founder_update','advisor_note','follow_up_response')),
source_type text not null,
raw_text text not null,
source_url text,
source_hash text not null,
created_at timestamptz not null default now(),
unique (founder_id, source_hash)
);

create table if not exists observations (
id uuid primary key default gen_random_uuid(),
founder_id uuid not null references founders(id) on delete cascade,
interaction_id uuid not null references interactions(id) on delete cascade,
category text not null,
claim text not null,
evidence_quote text not null,
evidence_type text not null check (evidence_type in ('reported_fact','customer_signal','behavioral_evidence','metric','advisor_observation','founder_belief')),
confidence numeric(4,3) not null check (confidence >= 0.6 and confidence <= 1.0),
occurred_at timestamptz not null,
created_at timestamptz not null default now()
);

create table if not exists assumptions (
id uuid primary key default gen_random_uuid(),
founder_id uuid not null references founders(id) on delete cascade,
statement text not null,
domain text not null,
status text not null check (status in ('untested','testing','supported','weakened','invalidated','superseded')),
confidence numeric(4,3) not null check (confidence >= 0 and confidence <= 1.0),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);

create table if not exists decisions (
id uuid primary key default gen_random_uuid(),
founder_id uuid not null references founders(id) on delete cascade,
interaction_id uuid not null references interactions(id) on delete cascade,
decision text not null,
rationale text,
created_at timestamptz not null default now()
);

create table if not exists commitments (
id uuid primary key default gen_random_uuid(),
founder_id uuid not null references founders(id) on delete cascade,
interaction_id uuid not null references interactions(id) on delete cascade,
owner text not null,
action text not null,
success_criterion text,
due_at timestamptz,
status text not null default 'open' check (status in ('open','completed','blocked','abandoned','superseded')),
completed_at timestamptz,
blocker text,
created_at timestamptz not null default now(),
check ((status = 'completed' and completed_at is not null) or status <> 'completed')
);

create table if not exists experiments (
id uuid primary key default gen_random_uuid(),
founder_id uuid not null references founders(id) on delete cascade,
interaction_id uuid not null references interactions(id) on delete cascade,
hypothesis text not null,
method text not null,
success_threshold text,
deadline timestamptz,
status text not null default 'planned' check (status in ('planned','running','completed','inconclusive','cancelled')),
result text,
learning text,
created_at timestamptz not null default now(),
completed_at timestamptz
);

create table if not exists founder_state (
founder_id uuid primary key references founders(id) on delete cascade,
state_version integer not null default 0 check (state_version >= 0),
primary_bottleneck text,
current_goal text,
current_milestone text,
icp_state text,
problem_state text,
wtp_state text,
traction_state text,
distribution_state text,
execution_state text,
open_questions jsonb not null default '[]'::jsonb,
key_risks jsonb not null default '[]'::jsonb,
last_updated_at timestamptz not null default now()
);

create index if not exists idx_interactions_founder_time on interactions(founder_id, occurred_at desc);
create index if not exists idx_observations_founder_time on observations(founder_id, occurred_at desc);
create index if not exists idx_commitments_founder_status_due on commitments(founder_id, status, due_at);
create index if not exists idx_experiments_founder_status on experiments(founder_id, status);

-- Overdue is derived, never a canonical commitment status.
create or replace view overdue_commitments as
select *
from commitments
where status = 'open' and due_at is not null and due_at < now();
Loading