Add research workspace and graphing flows
This commit is contained in:
@@ -30,6 +30,18 @@ type TaxonomyMetricValidationStatus = 'not_run' | 'matched' | 'mismatch' | 'erro
|
||||
type CoverageStatus = 'backlog' | 'active' | 'watch' | 'archive';
|
||||
type CoveragePriority = 'low' | 'medium' | 'high';
|
||||
type ResearchJournalEntryType = 'note' | 'filing_note' | 'status_change';
|
||||
type ResearchArtifactKind = 'filing' | 'ai_report' | 'note' | 'upload' | 'memo_snapshot' | 'status_change';
|
||||
type ResearchArtifactSource = 'system' | 'user';
|
||||
type ResearchVisibilityScope = 'private' | 'organization';
|
||||
type ResearchMemoRating = 'strong_buy' | 'buy' | 'hold' | 'sell';
|
||||
type ResearchMemoConviction = 'low' | 'medium' | 'high';
|
||||
type ResearchMemoSection =
|
||||
| 'thesis'
|
||||
| 'variant_view'
|
||||
| 'catalysts'
|
||||
| 'risks'
|
||||
| 'disconfirming_evidence'
|
||||
| 'next_actions';
|
||||
type FinancialCadence = 'annual' | 'quarterly' | 'ltm';
|
||||
type FinancialSurfaceKind =
|
||||
| 'income_statement'
|
||||
@@ -570,6 +582,72 @@ export const researchJournalEntry = sqliteTable('research_journal_entry', {
|
||||
researchJournalAccessionIndex: index('research_journal_accession_idx').on(table.user_id, table.accession_number)
|
||||
}));
|
||||
|
||||
export const researchArtifact = sqliteTable('research_artifact', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
|
||||
organization_id: text('organization_id').references(() => organization.id, { onDelete: 'set null' }),
|
||||
ticker: text('ticker').notNull(),
|
||||
accession_number: text('accession_number'),
|
||||
kind: text('kind').$type<ResearchArtifactKind>().notNull(),
|
||||
source: text('source').$type<ResearchArtifactSource>().notNull().default('user'),
|
||||
subtype: text('subtype'),
|
||||
title: text('title'),
|
||||
summary: text('summary'),
|
||||
body_markdown: text('body_markdown'),
|
||||
search_text: text('search_text'),
|
||||
visibility_scope: text('visibility_scope').$type<ResearchVisibilityScope>().notNull().default('private'),
|
||||
tags: text('tags', { mode: 'json' }).$type<string[]>(),
|
||||
metadata: text('metadata', { mode: 'json' }).$type<Record<string, unknown> | null>(),
|
||||
file_name: text('file_name'),
|
||||
mime_type: text('mime_type'),
|
||||
file_size_bytes: integer('file_size_bytes'),
|
||||
storage_path: text('storage_path'),
|
||||
created_at: text('created_at').notNull(),
|
||||
updated_at: text('updated_at').notNull()
|
||||
}, (table) => ({
|
||||
researchArtifactTickerIndex: index('research_artifact_ticker_idx').on(table.user_id, table.ticker, table.updated_at),
|
||||
researchArtifactKindIndex: index('research_artifact_kind_idx').on(table.user_id, table.kind, table.updated_at),
|
||||
researchArtifactAccessionIndex: index('research_artifact_accession_idx').on(table.user_id, table.accession_number),
|
||||
researchArtifactSourceIndex: index('research_artifact_source_idx').on(table.user_id, table.source, table.updated_at)
|
||||
}));
|
||||
|
||||
export const researchMemo = sqliteTable('research_memo', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
user_id: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
|
||||
organization_id: text('organization_id').references(() => organization.id, { onDelete: 'set null' }),
|
||||
ticker: text('ticker').notNull(),
|
||||
rating: text('rating').$type<ResearchMemoRating>(),
|
||||
conviction: text('conviction').$type<ResearchMemoConviction>(),
|
||||
time_horizon_months: integer('time_horizon_months'),
|
||||
packet_title: text('packet_title'),
|
||||
packet_subtitle: text('packet_subtitle'),
|
||||
thesis_markdown: text('thesis_markdown').notNull().default(''),
|
||||
variant_view_markdown: text('variant_view_markdown').notNull().default(''),
|
||||
catalysts_markdown: text('catalysts_markdown').notNull().default(''),
|
||||
risks_markdown: text('risks_markdown').notNull().default(''),
|
||||
disconfirming_evidence_markdown: text('disconfirming_evidence_markdown').notNull().default(''),
|
||||
next_actions_markdown: text('next_actions_markdown').notNull().default(''),
|
||||
created_at: text('created_at').notNull(),
|
||||
updated_at: text('updated_at').notNull()
|
||||
}, (table) => ({
|
||||
researchMemoTickerUnique: uniqueIndex('research_memo_ticker_uidx').on(table.user_id, table.ticker),
|
||||
researchMemoUpdatedIndex: index('research_memo_updated_idx').on(table.user_id, table.updated_at)
|
||||
}));
|
||||
|
||||
export const researchMemoEvidence = sqliteTable('research_memo_evidence', {
|
||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||
memo_id: integer('memo_id').notNull().references(() => researchMemo.id, { onDelete: 'cascade' }),
|
||||
artifact_id: integer('artifact_id').notNull().references(() => researchArtifact.id, { onDelete: 'cascade' }),
|
||||
section: text('section').$type<ResearchMemoSection>().notNull(),
|
||||
annotation: text('annotation'),
|
||||
sort_order: integer('sort_order').notNull().default(0),
|
||||
created_at: text('created_at').notNull()
|
||||
}, (table) => ({
|
||||
researchMemoEvidenceMemoIndex: index('research_memo_evidence_memo_idx').on(table.memo_id, table.section, table.sort_order),
|
||||
researchMemoEvidenceArtifactIndex: index('research_memo_evidence_artifact_idx').on(table.artifact_id),
|
||||
researchMemoEvidenceUnique: uniqueIndex('research_memo_evidence_unique_uidx').on(table.memo_id, table.artifact_id, table.section)
|
||||
}));
|
||||
|
||||
export const authSchema = {
|
||||
user,
|
||||
session,
|
||||
@@ -595,7 +673,10 @@ export const appSchema = {
|
||||
taskRun,
|
||||
taskStageEvent,
|
||||
portfolioInsight,
|
||||
researchJournalEntry
|
||||
researchJournalEntry,
|
||||
researchArtifact,
|
||||
researchMemo,
|
||||
researchMemoEvidence
|
||||
};
|
||||
|
||||
export const schema = {
|
||||
|
||||
Reference in New Issue
Block a user