@foundrynorth/flux-schema 1.17.3 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/schema.d.ts +2621 -4165
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +267 -225
- package/dist/schema.js.map +1 -1
- package/package.json +1 -1
package/dist/schema.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* CRITICAL: No stubs. All tables are real and will be created on migration.
|
|
8
8
|
*/
|
|
9
|
-
import { pgTable, varchar, text, boolean, timestamp, date, jsonb, integer, bigint, numeric, uuid, real, serial, time, pgEnum, uniqueIndex, index, customType, } from "drizzle-orm/pg-core";
|
|
9
|
+
import { pgTable, varchar, text, boolean, timestamp, date, jsonb, integer, bigint, bigserial, numeric, uuid, real, serial, time, pgEnum, uniqueIndex, index, customType, } from "drizzle-orm/pg-core";
|
|
10
10
|
import { relations, sql } from "drizzle-orm";
|
|
11
11
|
/**
|
|
12
12
|
* Custom pgvector type for embedding columns.
|
|
@@ -512,6 +512,7 @@ export const fluxAlerts = pgTable("flux_alerts", {
|
|
|
512
512
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
513
513
|
}, (table) => ({
|
|
514
514
|
projectIdx: index("flux_alerts_project_idx").on(table.projectId),
|
|
515
|
+
ruleIdx: index("flux_alerts_rule_idx").on(table.ruleId),
|
|
515
516
|
firedAtIdx: index("flux_alerts_fired_at_idx").on(table.firedAt),
|
|
516
517
|
unacknowledgedIdx: index("flux_alerts_unacknowledged_idx").on(table.acknowledgedAt),
|
|
517
518
|
}));
|
|
@@ -2570,6 +2571,20 @@ export const fluxFulfillmentTickets = pgTable("flux_fulfillment_tickets", {
|
|
|
2570
2571
|
dealOrderTotal: numeric("deal_order_total", { precision: 12, scale: 2 }),
|
|
2571
2572
|
/** Compass order status (e.g., "signed", "draft", "amended") */
|
|
2572
2573
|
compassOrderStatus: text("compass_order_status"),
|
|
2574
|
+
/** Human-readable product family name derived from Compass context */
|
|
2575
|
+
familyName: text("family_name"),
|
|
2576
|
+
/** Requested creative sizes from Compass / HubSpot line item context */
|
|
2577
|
+
adSizes: jsonb("ad_sizes").$type(),
|
|
2578
|
+
/** Product / offer description for stronger creative briefs */
|
|
2579
|
+
productDescription: text("product_description"),
|
|
2580
|
+
/** Product KPIs / use cases carried from upstream when available */
|
|
2581
|
+
productKpis: jsonb("product_kpis").$type(),
|
|
2582
|
+
/** Creative refresh cadence inherited from upstream order defaults */
|
|
2583
|
+
creativeRefreshCadence: text("creative_refresh_cadence"),
|
|
2584
|
+
/** Company domain used for landing page and brand context */
|
|
2585
|
+
companyDomain: text("company_domain"),
|
|
2586
|
+
/** Company industry used for briefing and brand alignment */
|
|
2587
|
+
companyIndustry: text("company_industry"),
|
|
2573
2588
|
// -- Native HubSpot SLA --
|
|
2574
2589
|
/** HubSpot SLA status for first response (Overdue, Due Soon, Active SLA, etc.) */
|
|
2575
2590
|
hubspotSlaFirstResponse: varchar("hubspot_sla_first_response"),
|
|
@@ -4046,26 +4061,46 @@ export const fluxSlaAuditLog = pgTable("flux_sla_audit_log", {
|
|
|
4046
4061
|
// Agreement Tracking
|
|
4047
4062
|
// ============================================================================
|
|
4048
4063
|
/**
|
|
4049
|
-
* Agreement tier
|
|
4064
|
+
* Agreement tier discriminator. Contracts are the umbrella commitment,
|
|
4065
|
+
* IOs are tactical executions (usually under a parent contract), NDAs
|
|
4066
|
+
* are non-disclosure documents with no commitment.
|
|
4050
4067
|
*/
|
|
4051
|
-
export const
|
|
4068
|
+
export const fluxAgreementTier = pgEnum("flux_agreement_tier", [
|
|
4052
4069
|
"contract",
|
|
4053
4070
|
"io",
|
|
4054
|
-
"
|
|
4071
|
+
"nda",
|
|
4055
4072
|
]);
|
|
4056
4073
|
/**
|
|
4057
|
-
*
|
|
4074
|
+
* Signing workflow state machine (pre-execution).
|
|
4058
4075
|
*/
|
|
4059
|
-
export const
|
|
4076
|
+
export const fluxSigningStatus = pgEnum("flux_signing_status", [
|
|
4060
4077
|
"draft",
|
|
4061
4078
|
"pending_internal",
|
|
4062
4079
|
"pending_client",
|
|
4063
4080
|
"pending_countersign",
|
|
4064
|
-
"
|
|
4081
|
+
"signed",
|
|
4082
|
+
"voided",
|
|
4083
|
+
]);
|
|
4084
|
+
/**
|
|
4085
|
+
* Post-signing lifecycle state machine. Separate from signing_status so
|
|
4086
|
+
* the two concerns don't interleave in a single enum.
|
|
4087
|
+
*/
|
|
4088
|
+
export const fluxLifecycleStatus = pgEnum("flux_lifecycle_status", [
|
|
4089
|
+
"pre_active",
|
|
4065
4090
|
"active",
|
|
4066
|
-
"
|
|
4091
|
+
"expiring_soon",
|
|
4067
4092
|
"expired",
|
|
4068
|
-
"
|
|
4093
|
+
"renewed",
|
|
4094
|
+
"terminated_early",
|
|
4095
|
+
]);
|
|
4096
|
+
/**
|
|
4097
|
+
* Relationship between a version chain entry and its predecessor.
|
|
4098
|
+
* NULL = original (not a revision). 'amendment' = revision of the same
|
|
4099
|
+
* commitment period. 'renewal' = sequential new commitment period.
|
|
4100
|
+
*/
|
|
4101
|
+
export const fluxRelationType = pgEnum("flux_relation_type", [
|
|
4102
|
+
"amendment",
|
|
4103
|
+
"renewal",
|
|
4069
4104
|
]);
|
|
4070
4105
|
/**
|
|
4071
4106
|
* Signer role in the signing workflow
|
|
@@ -4085,29 +4120,69 @@ export const fluxSignerStatusEnum = pgEnum("flux_signer_status", [
|
|
|
4085
4120
|
"delegated",
|
|
4086
4121
|
]);
|
|
4087
4122
|
/**
|
|
4088
|
-
* Agreements —
|
|
4089
|
-
*
|
|
4123
|
+
* Agreements — unified table for contracts, insertion orders, and NDAs.
|
|
4124
|
+
* Discriminated by `tier`. fn-flux is the system of record for this
|
|
4125
|
+
* entity; Compass keeps shadow rows in its own `contracts` table via
|
|
4126
|
+
* the `fluxAgreementId` back-reference.
|
|
4127
|
+
*
|
|
4128
|
+
* See docs/superpowers/specs/2026-04-10-contract-agreement-p0-design.md
|
|
4129
|
+
* for the full design rationale.
|
|
4090
4130
|
*/
|
|
4091
4131
|
export const fluxAgreements = pgTable("flux_agreements", {
|
|
4132
|
+
// ─── Identity + discriminator ─────────────────────────────────────
|
|
4092
4133
|
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4093
|
-
tier:
|
|
4094
|
-
|
|
4134
|
+
tier: fluxAgreementTier("tier").notNull(),
|
|
4135
|
+
version: integer("version").notNull().default(1),
|
|
4136
|
+
previousVersionId: text("previous_version_id").references(() => fluxAgreements.id),
|
|
4137
|
+
relationType: fluxRelationType("relation_type"),
|
|
4138
|
+
// ─── Two status state machines ─────────────────────────────────────
|
|
4139
|
+
signingStatus: fluxSigningStatus("signing_status")
|
|
4140
|
+
.notNull()
|
|
4141
|
+
.default("draft"),
|
|
4142
|
+
lifecycleStatus: fluxLifecycleStatus("lifecycle_status"),
|
|
4143
|
+
// ─── Counterparty (canonical: hubspot_company_id) ─────────────────
|
|
4144
|
+
hubspotCompanyId: text("hubspot_company_id"),
|
|
4145
|
+
counterpartyName: text("counterparty_name"),
|
|
4095
4146
|
projectId: text("project_id").references(() => fluxProjects.id),
|
|
4096
|
-
dealId: text("deal_id").references(() => fluxDealPipeline.id),
|
|
4097
4147
|
hubspotDealId: text("hubspot_deal_id"),
|
|
4098
|
-
|
|
4148
|
+
// ─── Hierarchy ─────────────────────────────────────────────────────
|
|
4149
|
+
parentAgreementId: text("parent_agreement_id").references(() => fluxAgreements.id),
|
|
4150
|
+
pendingCompassContractIdHint: text("pending_compass_contract_id_hint"),
|
|
4151
|
+
standaloneConfirmedAt: timestamp("standalone_confirmed_at", {
|
|
4152
|
+
withTimezone: true,
|
|
4153
|
+
}),
|
|
4154
|
+
standaloneConfirmedByUserId: text("standalone_confirmed_by_user_id").references(() => fluxUsers.id),
|
|
4155
|
+
// ─── Cross-system identity ─────────────────────────────────────────
|
|
4099
4156
|
compassContractId: text("compass_contract_id"),
|
|
4100
4157
|
compassOrderId: text("compass_order_id"),
|
|
4158
|
+
fulfillmentTicketId: text("fulfillment_ticket_id").references(() => fluxFulfillmentTickets.id),
|
|
4159
|
+
// ─── Terms ─────────────────────────────────────────────────────────
|
|
4101
4160
|
title: text("title").notNull(),
|
|
4102
4161
|
description: text("description"),
|
|
4103
|
-
commitmentAmount: numeric("commitment_amount", { precision: 14, scale: 2 }),
|
|
4104
|
-
tierCode: text("tier_code"),
|
|
4105
|
-
currency: text("currency").default("USD"),
|
|
4106
4162
|
effectiveDate: date("effective_date"),
|
|
4107
4163
|
expirationDate: date("expiration_date"),
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4164
|
+
currency: text("currency").notNull().default("USD"),
|
|
4165
|
+
entitlementTierCode: text("entitlement_tier_code"),
|
|
4166
|
+
// ─── Commitment + 3-stage drawdown (contract-tier only) ───────────
|
|
4167
|
+
commitmentAmount: numeric("commitment_amount", { precision: 14, scale: 2 }),
|
|
4168
|
+
committedAmount: numeric("committed_amount", { precision: 14, scale: 2 }),
|
|
4169
|
+
allocatedAmount: numeric("allocated_amount", { precision: 14, scale: 2 }),
|
|
4170
|
+
billedAmount: numeric("billed_amount", { precision: 14, scale: 2 }),
|
|
4171
|
+
remainingAmount: numeric("remaining_amount", { precision: 14, scale: 2 }),
|
|
4172
|
+
// ─── Margin (contract-tier only) ──────────────────────────────────
|
|
4173
|
+
projectedMarginPct: numeric("projected_margin_pct", { precision: 5, scale: 2 }),
|
|
4174
|
+
projectedMarginDollars: numeric("projected_margin_dollars", { precision: 14, scale: 2 }),
|
|
4175
|
+
actualMarginPct: numeric("actual_margin_pct", { precision: 5, scale: 2 }),
|
|
4176
|
+
actualMarginDollars: numeric("actual_margin_dollars", { precision: 14, scale: 2 }),
|
|
4177
|
+
// ─── Value-add, rate card, product mix (contract-tier only) ───────
|
|
4178
|
+
valueAddTotal: numeric("value_add_total", { precision: 14, scale: 2 }),
|
|
4179
|
+
rateCard: jsonb("rate_card"),
|
|
4180
|
+
productAllocations: jsonb("product_allocations"),
|
|
4181
|
+
// ─── Renewal terms (contract-tier only) ───────────────────────────
|
|
4182
|
+
autorenew: boolean("autorenew").notNull().default(false),
|
|
4183
|
+
renewalTermMonths: integer("renewal_term_months"),
|
|
4184
|
+
noticeDeadline: date("notice_deadline"),
|
|
4185
|
+
// ─── E-sign + document (all tiers) ────────────────────────────────
|
|
4111
4186
|
documentUrl: text("document_url"),
|
|
4112
4187
|
documentFingerprint: text("document_fingerprint"),
|
|
4113
4188
|
documentFileName: text("document_file_name"),
|
|
@@ -4115,31 +4190,19 @@ export const fluxAgreements = pgTable("flux_agreements", {
|
|
|
4115
4190
|
esignEnvelopeId: text("esign_envelope_id"),
|
|
4116
4191
|
esignCallbackUrl: text("esign_callback_url"),
|
|
4117
4192
|
esignStatus: text("esign_status"),
|
|
4118
|
-
|
|
4119
|
-
withTimezone: true,
|
|
4120
|
-
}),
|
|
4121
|
-
slackChannelId: text("slack_channel_id"),
|
|
4122
|
-
slackMessageTs: text("slack_message_ts"),
|
|
4193
|
+
// ─── Health / risk / compliance (feeds P4) ────────────────────────
|
|
4123
4194
|
healthScore: integer("health_score"),
|
|
4124
|
-
complianceStatus: text("compliance_status"),
|
|
4125
|
-
riskIndicators: jsonb("risk_indicators").default([]),
|
|
4126
|
-
parentContractId: text("parent_contract_id").references(() => fluxMasterContracts.id),
|
|
4127
|
-
metadata: jsonb("metadata").default({}),
|
|
4128
|
-
// --- Workspace columns (graduated from metadata JSONB) ---
|
|
4129
|
-
priority: text("priority").default("medium"),
|
|
4130
|
-
workspaceStatus: text("workspace_status"),
|
|
4131
|
-
counterpartyName: text("counterparty_name"),
|
|
4132
|
-
currentModule: text("current_module"),
|
|
4133
|
-
receivedAt: timestamp("received_at", { withTimezone: true }),
|
|
4134
|
-
requestedBy: text("requested_by"),
|
|
4135
|
-
ownerName: text("owner_name"),
|
|
4136
|
-
needsBy: text("needs_by"),
|
|
4137
|
-
autorenew: boolean("autorenew"),
|
|
4138
|
-
renewalTermMonths: integer("renewal_term_months"),
|
|
4139
|
-
noticeDeadline: text("notice_deadline"),
|
|
4140
|
-
packageType: jsonb("package_type").default([]),
|
|
4141
|
-
tags: jsonb("tags").default([]),
|
|
4142
4195
|
riskScore: integer("risk_score"),
|
|
4196
|
+
complianceStatus: text("compliance_status"),
|
|
4197
|
+
riskIndicators: jsonb("risk_indicators").notNull().default([]),
|
|
4198
|
+
// ─── Operational ──────────────────────────────────────────────────
|
|
4199
|
+
priority: text("priority").notNull().default("medium"),
|
|
4200
|
+
slackChannelId: text("slack_channel_id"),
|
|
4201
|
+
slackMessageTs: text("slack_message_ts"),
|
|
4202
|
+
tags: jsonb("tags").notNull().default([]),
|
|
4203
|
+
amendmentReason: text("amendment_reason"),
|
|
4204
|
+
metadata: jsonb("metadata").notNull().default({}),
|
|
4205
|
+
// ─── Audit ────────────────────────────────────────────────────────
|
|
4143
4206
|
createdById: text("created_by_id").references(() => fluxUsers.id),
|
|
4144
4207
|
createdAt: timestamp("created_at", { withTimezone: true })
|
|
4145
4208
|
.notNull()
|
|
@@ -4147,16 +4210,52 @@ export const fluxAgreements = pgTable("flux_agreements", {
|
|
|
4147
4210
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
4148
4211
|
.notNull()
|
|
4149
4212
|
.defaultNow(),
|
|
4150
|
-
|
|
4213
|
+
hubspotPropertySyncedAt: timestamp("hubspot_property_synced_at", {
|
|
4214
|
+
withTimezone: true,
|
|
4215
|
+
}),
|
|
4216
|
+
}, (table) => ({
|
|
4217
|
+
idxTierLifecycle: index("idx_flux_agreements_tier_lifecycle")
|
|
4218
|
+
.on(table.tier, table.lifecycleStatus)
|
|
4219
|
+
.where(sql `${table.lifecycleStatus} IS NOT NULL`),
|
|
4220
|
+
idxHubspotCompany: index("idx_flux_agreements_hubspot_company")
|
|
4221
|
+
.on(table.hubspotCompanyId)
|
|
4222
|
+
.where(sql `${table.hubspotCompanyId} IS NOT NULL`),
|
|
4223
|
+
idxParent: index("idx_flux_agreements_parent")
|
|
4224
|
+
.on(table.parentAgreementId)
|
|
4225
|
+
.where(sql `${table.parentAgreementId} IS NOT NULL`),
|
|
4226
|
+
idxCompassContract: index("idx_flux_agreements_compass_contract")
|
|
4227
|
+
.on(table.compassContractId)
|
|
4228
|
+
.where(sql `${table.compassContractId} IS NOT NULL`),
|
|
4229
|
+
idxCompassOrder: index("idx_flux_agreements_compass_order")
|
|
4230
|
+
.on(table.compassOrderId)
|
|
4231
|
+
.where(sql `${table.compassOrderId} IS NOT NULL`),
|
|
4232
|
+
idxSigningPending: index("idx_flux_agreements_signing_pending")
|
|
4233
|
+
.on(table.signingStatus)
|
|
4234
|
+
.where(sql `${table.signingStatus} IN ('pending_internal', 'pending_client', 'pending_countersign')`),
|
|
4235
|
+
idxExpiring: index("idx_flux_agreements_expiring")
|
|
4236
|
+
.on(table.expirationDate)
|
|
4237
|
+
.where(sql `${table.lifecycleStatus} IN ('active', 'expiring_soon')`),
|
|
4238
|
+
idxVersionChain: index("idx_flux_agreements_version_chain")
|
|
4239
|
+
.on(table.previousVersionId)
|
|
4240
|
+
.where(sql `${table.previousVersionId} IS NOT NULL`),
|
|
4241
|
+
idxUnlinkedIos: index("idx_flux_agreements_unlinked_ios")
|
|
4242
|
+
.on(table.tier, table.signingStatus, table.createdAt)
|
|
4243
|
+
.where(sql `${table.tier} = 'io' AND ${table.parentAgreementId} IS NULL AND ${table.standaloneConfirmedAt} IS NULL`),
|
|
4244
|
+
idxPendingHint: index("idx_flux_agreements_pending_hint")
|
|
4245
|
+
.on(table.pendingCompassContractIdHint)
|
|
4246
|
+
.where(sql `${table.pendingCompassContractIdHint} IS NOT NULL`),
|
|
4247
|
+
uqCompassContract: uniqueIndex("uq_flux_agreements_compass_contract")
|
|
4248
|
+
.on(table.compassContractId)
|
|
4249
|
+
.where(sql `${table.compassContractId} IS NOT NULL`),
|
|
4250
|
+
uqCompassOrder: uniqueIndex("uq_flux_agreements_compass_order")
|
|
4251
|
+
.on(table.compassOrderId)
|
|
4252
|
+
.where(sql `${table.compassOrderId} IS NOT NULL`),
|
|
4253
|
+
}));
|
|
4151
4254
|
export const fluxAgreementsRelations = relations(fluxAgreements, ({ one, many }) => ({
|
|
4152
4255
|
project: one(fluxProjects, {
|
|
4153
4256
|
fields: [fluxAgreements.projectId],
|
|
4154
4257
|
references: [fluxProjects.id],
|
|
4155
4258
|
}),
|
|
4156
|
-
deal: one(fluxDealPipeline, {
|
|
4157
|
-
fields: [fluxAgreements.dealId],
|
|
4158
|
-
references: [fluxDealPipeline.id],
|
|
4159
|
-
}),
|
|
4160
4259
|
fulfillmentTicket: one(fluxFulfillmentTickets, {
|
|
4161
4260
|
fields: [fluxAgreements.fulfillmentTicketId],
|
|
4162
4261
|
references: [fluxFulfillmentTickets.id],
|
|
@@ -4164,15 +4263,24 @@ export const fluxAgreementsRelations = relations(fluxAgreements, ({ one, many })
|
|
|
4164
4263
|
previousVersion: one(fluxAgreements, {
|
|
4165
4264
|
fields: [fluxAgreements.previousVersionId],
|
|
4166
4265
|
references: [fluxAgreements.id],
|
|
4266
|
+
relationName: "versionChain",
|
|
4267
|
+
}),
|
|
4268
|
+
parentAgreement: one(fluxAgreements, {
|
|
4269
|
+
fields: [fluxAgreements.parentAgreementId],
|
|
4270
|
+
references: [fluxAgreements.id],
|
|
4271
|
+
relationName: "parentChild",
|
|
4167
4272
|
}),
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
references: [fluxMasterContracts.id],
|
|
4273
|
+
childAgreements: many(fluxAgreements, {
|
|
4274
|
+
relationName: "parentChild",
|
|
4171
4275
|
}),
|
|
4172
4276
|
createdBy: one(fluxUsers, {
|
|
4173
4277
|
fields: [fluxAgreements.createdById],
|
|
4174
4278
|
references: [fluxUsers.id],
|
|
4175
4279
|
}),
|
|
4280
|
+
standaloneConfirmedBy: one(fluxUsers, {
|
|
4281
|
+
fields: [fluxAgreements.standaloneConfirmedByUserId],
|
|
4282
|
+
references: [fluxUsers.id],
|
|
4283
|
+
}),
|
|
4176
4284
|
signers: many(fluxAgreementSigners),
|
|
4177
4285
|
activity: many(fluxAgreementActivity),
|
|
4178
4286
|
comments: many(fluxAgreementComments),
|
|
@@ -4181,6 +4289,7 @@ export const fluxAgreementsRelations = relations(fluxAgreements, ({ one, many })
|
|
|
4181
4289
|
approvalRuns: many(fluxAgreementApprovalRuns),
|
|
4182
4290
|
esignRuns: many(fluxAgreementEsignRuns),
|
|
4183
4291
|
lifecycleEvents: many(fluxAgreementLifecycleEvents),
|
|
4292
|
+
agreementEvents: many(fluxAgreementEvents),
|
|
4184
4293
|
}));
|
|
4185
4294
|
/**
|
|
4186
4295
|
* Agreement signers — tracks each signer in the signing workflow.
|
|
@@ -4266,7 +4375,7 @@ export const fluxAgreementActivityRelations = relations(fluxAgreementActivity, (
|
|
|
4266
4375
|
*/
|
|
4267
4376
|
export const fluxEsignConfig = pgTable("flux_esign_config", {
|
|
4268
4377
|
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4269
|
-
tier:
|
|
4378
|
+
tier: fluxAgreementTier("tier").notNull().unique(),
|
|
4270
4379
|
firmaTemplateId: text("firma_template_id"),
|
|
4271
4380
|
defaultVpSignerIds: text("default_vp_signer_ids")
|
|
4272
4381
|
.array()
|
|
@@ -4762,118 +4871,114 @@ export const fluxSimplifiPacing = pgTable("flux_simplifi_pacing", {
|
|
|
4762
4871
|
endDateIdx: index("flux_simplifi_pacing_end_date_idx").on(table.endDate),
|
|
4763
4872
|
}));
|
|
4764
4873
|
// ============================================================================
|
|
4765
|
-
//
|
|
4874
|
+
// AGREEMENT SUPPORTING TABLES — Events, Inbound Dedupe, Versions, Chunks
|
|
4766
4875
|
// ============================================================================
|
|
4767
4876
|
/**
|
|
4768
|
-
*
|
|
4877
|
+
* flux_agreement_events — Internal audit log of every state change on a
|
|
4878
|
+
* flux_agreements row. Written within the same transaction as the
|
|
4879
|
+
* mutation via the agreement repository. Consumed by P4 risk scoring
|
|
4880
|
+
* and P5 alerting.
|
|
4769
4881
|
*/
|
|
4770
|
-
export const
|
|
4771
|
-
"
|
|
4772
|
-
"
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
"
|
|
4776
|
-
"
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4882
|
+
export const fluxAgreementEvents = pgTable("flux_agreement_events", {
|
|
4883
|
+
id: bigserial("id", { mode: "number" }).primaryKey(),
|
|
4884
|
+
agreementId: text("agreement_id")
|
|
4885
|
+
.notNull()
|
|
4886
|
+
.references(() => fluxAgreements.id),
|
|
4887
|
+
eventType: text("event_type").notNull(),
|
|
4888
|
+
previousValue: text("previous_value"),
|
|
4889
|
+
newValue: text("new_value"),
|
|
4890
|
+
actorUserId: text("actor_user_id").references(() => fluxUsers.id),
|
|
4891
|
+
reason: text("reason"),
|
|
4892
|
+
emittedAt: timestamp("emitted_at", { withTimezone: true })
|
|
4893
|
+
.notNull()
|
|
4894
|
+
.defaultNow(),
|
|
4895
|
+
}, (table) => ({
|
|
4896
|
+
byAgreementIdx: index("idx_flux_agreement_events_by_agreement").on(table.agreementId, table.emittedAt),
|
|
4897
|
+
byTypeIdx: index("idx_flux_agreement_events_by_type").on(table.eventType, table.emittedAt),
|
|
4898
|
+
}));
|
|
4899
|
+
export const fluxAgreementEventsRelations = relations(fluxAgreementEvents, ({ one }) => ({
|
|
4900
|
+
agreement: one(fluxAgreements, {
|
|
4901
|
+
fields: [fluxAgreementEvents.agreementId],
|
|
4902
|
+
references: [fluxAgreements.id],
|
|
4903
|
+
}),
|
|
4904
|
+
actor: one(fluxUsers, {
|
|
4905
|
+
fields: [fluxAgreementEvents.actorUserId],
|
|
4906
|
+
references: [fluxUsers.id],
|
|
4907
|
+
}),
|
|
4908
|
+
}));
|
|
4782
4909
|
/**
|
|
4783
|
-
*
|
|
4910
|
+
* flux_inbound_events — Dedupe log for events received from external
|
|
4911
|
+
* sources (Compass). Used by the legacy-event-ingest handlers to
|
|
4912
|
+
* gracefully skip retried events without double-processing. 30-day
|
|
4913
|
+
* retention via a prune cron (not in this schema; lives in fn-flux).
|
|
4784
4914
|
*/
|
|
4785
|
-
export const
|
|
4786
|
-
"
|
|
4787
|
-
"
|
|
4788
|
-
|
|
4915
|
+
export const fluxInboundEvents = pgTable("flux_inbound_events", {
|
|
4916
|
+
eventId: text("event_id").primaryKey(),
|
|
4917
|
+
source: text("source").notNull(),
|
|
4918
|
+
eventType: text("event_type").notNull(),
|
|
4919
|
+
receivedAt: timestamp("received_at", { withTimezone: true })
|
|
4920
|
+
.notNull()
|
|
4921
|
+
.defaultNow(),
|
|
4922
|
+
processingResult: text("processing_result").notNull().default("pending"),
|
|
4923
|
+
processingError: text("processing_error"),
|
|
4924
|
+
});
|
|
4789
4925
|
/**
|
|
4790
|
-
*
|
|
4791
|
-
*
|
|
4926
|
+
* flux_outbound_events — Outbox for events Flux sends to external systems
|
|
4927
|
+
* (currently only Compass at POST /flux-contract-events). The agreement
|
|
4928
|
+
* repository enqueues rows in the same transaction as the state mutation,
|
|
4929
|
+
* and a cron worker delivers them with exponential backoff + HMAC signing.
|
|
4930
|
+
*
|
|
4931
|
+
* Design goals:
|
|
4932
|
+
* - At-least-once delivery (cron retries until success or max attempts)
|
|
4933
|
+
* - Transactional consistency with the underlying mutation (if the tx rolls
|
|
4934
|
+
* back, the outbox row rolls back too — no phantom events)
|
|
4935
|
+
* - Idempotent on the receiver side via the stable event_id
|
|
4936
|
+
* - Observable: last_error + attempts for operator debugging
|
|
4937
|
+
*
|
|
4938
|
+
* Retention: delivered rows older than 7 days are pruned by a cron;
|
|
4939
|
+
* failed rows are kept indefinitely for forensic investigation.
|
|
4792
4940
|
*/
|
|
4793
|
-
export const
|
|
4941
|
+
export const fluxOutboundEvents = pgTable("flux_outbound_events", {
|
|
4794
4942
|
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
status: fluxMasterContractStatusEnum("status").notNull().default("draft"),
|
|
4807
|
-
tags: text("tags")
|
|
4808
|
-
.array()
|
|
4809
|
-
.default(sql `'{}'::text[]`),
|
|
4810
|
-
customFields: jsonb("custom_fields").default({}),
|
|
4811
|
-
notes: text("notes"),
|
|
4812
|
-
documentUrl: text("document_url"),
|
|
4813
|
-
documentFileName: text("document_file_name"),
|
|
4814
|
-
documentStorageKey: text("document_storage_key"),
|
|
4815
|
-
riskScore: integer("risk_score"),
|
|
4816
|
-
riskIndicators: jsonb("risk_indicators").default([]),
|
|
4817
|
-
// Financial fields — Phase: Contract Financial Alignment
|
|
4818
|
-
tierCode: text("tier_code"), // tier_1 | tier_2 | tier_3 | open
|
|
4819
|
-
rateCard: jsonb("rate_card"), // [{ stribProductId?, familyCode?, categoryCode?, rate, rateNotes? }]
|
|
4820
|
-
productAllocations: jsonb("product_allocations"), // [{ familyCode, familyName, planned, notes? }]
|
|
4821
|
-
billingCadence: text("billing_cadence"), // monthly | quarterly | annual
|
|
4822
|
-
termsUrl: text("terms_url"),
|
|
4823
|
-
termsAddendum: text("terms_addendum"),
|
|
4824
|
-
allocatedAmount: numeric("allocated_amount", { precision: 14, scale: 2 }),
|
|
4825
|
-
remainingAmount: numeric("remaining_amount", { precision: 14, scale: 2 }),
|
|
4826
|
-
projectedMarginPct: real("projected_margin_pct"),
|
|
4827
|
-
projectedMarginDollars: numeric("projected_margin_dollars", { precision: 14, scale: 2 }),
|
|
4828
|
-
actualMarginPct: real("actual_margin_pct"),
|
|
4829
|
-
actualMarginDollars: numeric("actual_margin_dollars", { precision: 14, scale: 2 }),
|
|
4830
|
-
valueAddTotal: numeric("value_add_total", { precision: 14, scale: 2 }),
|
|
4831
|
-
fluxVelocityProfileId: text("flux_velocity_profile_id"),
|
|
4832
|
-
customSlaDays: integer("custom_sla_days"),
|
|
4833
|
-
customWarnDays: integer("custom_warn_days"),
|
|
4834
|
-
customBlockDays: integer("custom_block_days"),
|
|
4835
|
-
orderCount: integer("order_count"),
|
|
4836
|
-
createdById: text("created_by_id").references(() => fluxUsers.id),
|
|
4837
|
-
projectId: text("project_id").references(() => fluxProjects.id),
|
|
4838
|
-
hubspotCompanyId: text("hubspot_company_id"),
|
|
4943
|
+
/** Stable event identifier used by the receiver for dedupe */
|
|
4944
|
+
eventId: text("event_id").notNull().unique(),
|
|
4945
|
+
/** 'compass' | 'hubspot' | future destinations */
|
|
4946
|
+
destination: text("destination").notNull(),
|
|
4947
|
+
/** Event type (e.g. 'contract.created', 'contract.updated') */
|
|
4948
|
+
eventType: text("event_type").notNull(),
|
|
4949
|
+
/** Optional FK-ish link to the agreement that triggered the event */
|
|
4950
|
+
fluxAgreementId: text("flux_agreement_id"),
|
|
4951
|
+
/** Full JSON payload posted to the receiver */
|
|
4952
|
+
payload: jsonb("payload").notNull(),
|
|
4953
|
+
/** When the row was created (same tx as the state mutation) */
|
|
4839
4954
|
createdAt: timestamp("created_at", { withTimezone: true })
|
|
4840
4955
|
.notNull()
|
|
4841
4956
|
.defaultNow(),
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4957
|
+
/** When the event was successfully delivered (NULL until confirmed 2xx) */
|
|
4958
|
+
deliveredAt: timestamp("delivered_at", { withTimezone: true }),
|
|
4959
|
+
/** Delivery attempt count — used for exponential backoff */
|
|
4960
|
+
attempts: integer("attempts").notNull().default(0),
|
|
4961
|
+
/** Last error string from a failed delivery attempt */
|
|
4962
|
+
lastError: text("last_error"),
|
|
4963
|
+
/** When the next retry should fire (NULL = ready now, past = retry overdue) */
|
|
4964
|
+
nextRetryAt: timestamp("next_retry_at", { withTimezone: true }),
|
|
4845
4965
|
}, (table) => ({
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
})
|
|
4852
|
-
export const fluxMasterContractsRelations = relations(fluxMasterContracts, ({ one, many }) => ({
|
|
4853
|
-
createdBy: one(fluxUsers, {
|
|
4854
|
-
fields: [fluxMasterContracts.createdById],
|
|
4855
|
-
references: [fluxUsers.id],
|
|
4856
|
-
}),
|
|
4857
|
-
project: one(fluxProjects, {
|
|
4858
|
-
fields: [fluxMasterContracts.projectId],
|
|
4859
|
-
references: [fluxProjects.id],
|
|
4860
|
-
}),
|
|
4861
|
-
childAgreements: many(fluxAgreements),
|
|
4862
|
-
versions: many(fluxContractVersions),
|
|
4863
|
-
pacing: many(fluxContractPacing),
|
|
4864
|
-
comments: many(fluxAgreementComments),
|
|
4966
|
+
pendingIdx: index("idx_flux_outbound_events_pending")
|
|
4967
|
+
.on(table.destination, table.nextRetryAt)
|
|
4968
|
+
.where(sql `${table.deliveredAt} IS NULL`),
|
|
4969
|
+
agreementIdx: index("idx_flux_outbound_events_agreement")
|
|
4970
|
+
.on(table.fluxAgreementId)
|
|
4971
|
+
.where(sql `${table.fluxAgreementId} IS NOT NULL`),
|
|
4865
4972
|
}));
|
|
4866
4973
|
/**
|
|
4867
|
-
* flux_contract_versions — Document version history for
|
|
4974
|
+
* flux_contract_versions — Document version history for agreements.
|
|
4868
4975
|
* Each revision stored as a separate R2 key with version number.
|
|
4869
4976
|
*/
|
|
4870
4977
|
export const fluxContractVersions = pgTable("flux_contract_versions", {
|
|
4871
4978
|
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
contractId: text("contract_id").notNull(),
|
|
4876
|
-
contractType: fluxContractSourceTypeEnum("contract_type").notNull(),
|
|
4979
|
+
agreementId: text("agreement_id")
|
|
4980
|
+
.notNull()
|
|
4981
|
+
.references(() => fluxAgreements.id, { onDelete: "cascade" }),
|
|
4877
4982
|
version: integer("version").notNull(),
|
|
4878
4983
|
storageKey: text("storage_key").notNull(),
|
|
4879
4984
|
fileName: text("file_name").notNull(),
|
|
@@ -4883,25 +4988,28 @@ export const fluxContractVersions = pgTable("flux_contract_versions", {
|
|
|
4883
4988
|
.notNull()
|
|
4884
4989
|
.defaultNow(),
|
|
4885
4990
|
}, (table) => ({
|
|
4886
|
-
contractIdx: index("flux_contract_versions_contract_idx").on(table.
|
|
4887
|
-
versionIdx: uniqueIndex("flux_contract_versions_unique_idx").on(table.
|
|
4991
|
+
contractIdx: index("flux_contract_versions_contract_idx").on(table.agreementId),
|
|
4992
|
+
versionIdx: uniqueIndex("flux_contract_versions_unique_idx").on(table.agreementId, table.version),
|
|
4888
4993
|
}));
|
|
4889
4994
|
export const fluxContractVersionsRelations = relations(fluxContractVersions, ({ one }) => ({
|
|
4995
|
+
agreement: one(fluxAgreements, {
|
|
4996
|
+
fields: [fluxContractVersions.agreementId],
|
|
4997
|
+
references: [fluxAgreements.id],
|
|
4998
|
+
}),
|
|
4890
4999
|
uploadedBy: one(fluxUsers, {
|
|
4891
5000
|
fields: [fluxContractVersions.uploadedById],
|
|
4892
5001
|
references: [fluxUsers.id],
|
|
4893
5002
|
}),
|
|
4894
5003
|
}));
|
|
4895
5004
|
/**
|
|
4896
|
-
* flux_agreement_comments — Threaded comments on agreements
|
|
5005
|
+
* flux_agreement_comments — Threaded comments on agreements.
|
|
4897
5006
|
* Supports clause references (page, paragraph, quote) for negotiation tracking.
|
|
4898
5007
|
*/
|
|
4899
5008
|
export const fluxAgreementComments = pgTable("flux_agreement_comments", {
|
|
4900
5009
|
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4901
|
-
agreementId: text("agreement_id")
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
contractId: text("contract_id").references(() => fluxMasterContracts.id, {
|
|
5010
|
+
agreementId: text("agreement_id")
|
|
5011
|
+
.notNull()
|
|
5012
|
+
.references(() => fluxAgreements.id, {
|
|
4905
5013
|
onDelete: "cascade",
|
|
4906
5014
|
}),
|
|
4907
5015
|
authorId: text("author_id").references(() => fluxUsers.id),
|
|
@@ -4914,7 +5022,6 @@ export const fluxAgreementComments = pgTable("flux_agreement_comments", {
|
|
|
4914
5022
|
.defaultNow(),
|
|
4915
5023
|
}, (table) => ({
|
|
4916
5024
|
agreementIdx: index("flux_agreement_comments_agreement_idx").on(table.agreementId),
|
|
4917
|
-
contractIdx: index("flux_agreement_comments_contract_idx").on(table.contractId),
|
|
4918
5025
|
parentIdx: index("flux_agreement_comments_parent_idx").on(table.parentCommentId),
|
|
4919
5026
|
}));
|
|
4920
5027
|
export const fluxAgreementCommentsRelations = relations(fluxAgreementComments, ({ one, many }) => ({
|
|
@@ -4922,10 +5029,6 @@ export const fluxAgreementCommentsRelations = relations(fluxAgreementComments, (
|
|
|
4922
5029
|
fields: [fluxAgreementComments.agreementId],
|
|
4923
5030
|
references: [fluxAgreements.id],
|
|
4924
5031
|
}),
|
|
4925
|
-
contract: one(fluxMasterContracts, {
|
|
4926
|
-
fields: [fluxAgreementComments.contractId],
|
|
4927
|
-
references: [fluxMasterContracts.id],
|
|
4928
|
-
}),
|
|
4929
5032
|
author: one(fluxUsers, {
|
|
4930
5033
|
fields: [fluxAgreementComments.authorId],
|
|
4931
5034
|
references: [fluxUsers.id],
|
|
@@ -4940,14 +5043,15 @@ export const fluxAgreementCommentsRelations = relations(fluxAgreementComments, (
|
|
|
4940
5043
|
}),
|
|
4941
5044
|
}));
|
|
4942
5045
|
/**
|
|
4943
|
-
* flux_contract_chunks — pgvector-indexed
|
|
5046
|
+
* flux_contract_chunks — pgvector-indexed agreement content for AI search.
|
|
4944
5047
|
* Embeddings via OpenAI text-embedding-3-large (1536 dimensions).
|
|
4945
5048
|
* Hybrid search: vector cosine + FTS via websearch_to_tsquery.
|
|
4946
5049
|
*/
|
|
4947
5050
|
export const fluxContractChunks = pgTable("flux_contract_chunks", {
|
|
4948
5051
|
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4949
|
-
|
|
4950
|
-
|
|
5052
|
+
agreementId: text("agreement_id")
|
|
5053
|
+
.notNull()
|
|
5054
|
+
.references(() => fluxAgreements.id, { onDelete: "cascade" }),
|
|
4951
5055
|
chunkIndex: integer("chunk_index").notNull(),
|
|
4952
5056
|
chunkText: text("chunk_text").notNull(),
|
|
4953
5057
|
embedding: vector("embedding", { dimensions: 1536 }),
|
|
@@ -4956,7 +5060,7 @@ export const fluxContractChunks = pgTable("flux_contract_chunks", {
|
|
|
4956
5060
|
.notNull()
|
|
4957
5061
|
.defaultNow(),
|
|
4958
5062
|
}, (table) => ({
|
|
4959
|
-
contractIdx: index("flux_contract_chunks_contract_idx").on(table.
|
|
5063
|
+
contractIdx: index("flux_contract_chunks_contract_idx").on(table.agreementId),
|
|
4960
5064
|
metadataIdx: index("flux_contract_chunks_metadata_idx")
|
|
4961
5065
|
.using("gin", table.metadata),
|
|
4962
5066
|
}));
|
|
@@ -4978,68 +5082,6 @@ export const fluxContractPolicyChunks = pgTable("flux_contract_policy_chunks", {
|
|
|
4978
5082
|
}, (table) => ({
|
|
4979
5083
|
policyIdx: index("flux_contract_policy_chunks_policy_idx").on(table.policyName),
|
|
4980
5084
|
}));
|
|
4981
|
-
/**
|
|
4982
|
-
* flux_contract_pacing — Computed drawdown pacing per active master contract.
|
|
4983
|
-
* Mirrors flux_tapclicks_pacing pattern: velocity, trend, anomaly, status.
|
|
4984
|
-
*/
|
|
4985
|
-
export const fluxContractPacing = pgTable("flux_contract_pacing", {
|
|
4986
|
-
id: text("id").primaryKey().default(sql `gen_random_uuid()::text`),
|
|
4987
|
-
contractId: text("contract_id")
|
|
4988
|
-
.notNull()
|
|
4989
|
-
.references(() => fluxMasterContracts.id),
|
|
4990
|
-
pacingStatus: text("pacing_status").notNull(),
|
|
4991
|
-
pacingPercent: numeric("pacing_percent", { precision: 8, scale: 2 }),
|
|
4992
|
-
dailyVelocity: numeric("daily_velocity", { precision: 14, scale: 2 }),
|
|
4993
|
-
velocityTrend: text("velocity_trend"),
|
|
4994
|
-
totalDrawnDown: numeric("total_drawn_down", { precision: 14, scale: 2 }),
|
|
4995
|
-
remainingCapacity: numeric("remaining_capacity", {
|
|
4996
|
-
precision: 14,
|
|
4997
|
-
scale: 2,
|
|
4998
|
-
}),
|
|
4999
|
-
projectedEndSpend: numeric("projected_end_spend", {
|
|
5000
|
-
precision: 14,
|
|
5001
|
-
scale: 2,
|
|
5002
|
-
}),
|
|
5003
|
-
runwayDays: integer("runway_days"),
|
|
5004
|
-
anomalies: jsonb("anomalies").default([]),
|
|
5005
|
-
// ── Contract remainder forecast columns (populated by pacing cron) ──
|
|
5006
|
-
/** Extrapolated margin for unordered remainder (actualMargin - decay) */
|
|
5007
|
-
extrapolatedMarginPct: real("extrapolated_margin_pct"),
|
|
5008
|
-
/** Contract-level confidence score for remainder */
|
|
5009
|
-
forecastConfidence: real("forecast_confidence"),
|
|
5010
|
-
/** Confidence tier for remainder: commit/likely/upside/longshot */
|
|
5011
|
-
forecastConfidenceTier: text("forecast_confidence_tier"),
|
|
5012
|
-
/** Remainder revenue forecasts by window */
|
|
5013
|
-
remainderForecast30d: numeric("remainder_forecast_30d", { precision: 14, scale: 2 }),
|
|
5014
|
-
remainderForecast60d: numeric("remainder_forecast_60d", { precision: 14, scale: 2 }),
|
|
5015
|
-
remainderForecast90d: numeric("remainder_forecast_90d", { precision: 14, scale: 2 }),
|
|
5016
|
-
/** Remainder margin forecasts by window */
|
|
5017
|
-
remainderMarginForecast30d: numeric("remainder_margin_forecast_30d", { precision: 14, scale: 2 }),
|
|
5018
|
-
remainderMarginForecast60d: numeric("remainder_margin_forecast_60d", { precision: 14, scale: 2 }),
|
|
5019
|
-
remainderMarginForecast90d: numeric("remainder_margin_forecast_90d", { precision: 14, scale: 2 }),
|
|
5020
|
-
/** Periods until next expected IO */
|
|
5021
|
-
periodDistance: integer("period_distance"),
|
|
5022
|
-
/** Periods remaining on contract */
|
|
5023
|
-
periodsRemaining: integer("periods_remaining"),
|
|
5024
|
-
computedAt: timestamp("computed_at", { withTimezone: true })
|
|
5025
|
-
.notNull()
|
|
5026
|
-
.defaultNow(),
|
|
5027
|
-
createdAt: timestamp("created_at", { withTimezone: true })
|
|
5028
|
-
.notNull()
|
|
5029
|
-
.defaultNow(),
|
|
5030
|
-
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
5031
|
-
.notNull()
|
|
5032
|
-
.defaultNow(),
|
|
5033
|
-
}, (table) => ({
|
|
5034
|
-
contractIdx: uniqueIndex("flux_contract_pacing_contract_idx").on(table.contractId),
|
|
5035
|
-
statusIdx: index("flux_contract_pacing_status_idx").on(table.pacingStatus),
|
|
5036
|
-
}));
|
|
5037
|
-
export const fluxContractPacingRelations = relations(fluxContractPacing, ({ one }) => ({
|
|
5038
|
-
contract: one(fluxMasterContracts, {
|
|
5039
|
-
fields: [fluxContractPacing.contractId],
|
|
5040
|
-
references: [fluxMasterContracts.id],
|
|
5041
|
-
}),
|
|
5042
|
-
}));
|
|
5043
5085
|
// ---------------------------------------------------------------------------
|
|
5044
5086
|
// Agreement Workspace Tables — graduated from metadata JSONB
|
|
5045
5087
|
// ---------------------------------------------------------------------------
|