Make mutations confirm themselves, and seed the evidence trail
CI / verify (push) Successful in 2m51s

Two demo gaps, both of which made working features look like they were not
there.

**Toasts fired into nothing.** RecordSheets already called toast.success on
every save, but <Toaster /> was never mounted, so nothing appeared. It could
not be mounted, either: the shadcn original imports next-themes, which PIG does
not use — it has its own provider so a chosen theme is persisted server-side
and follows a user between devices. Rewired to PIG's useTheme, mounted inside
ThemeProvider, and offset clear of the phone tab bar and the home indicator.

Feedback added where the interface otherwise gives none: allocation and hold
report the GPU-hours actually written, because the sheet closes on success and
the only other evidence is a number moving off-screen; releasing a hold says
the capacity is sellable again; fact decisions say what the decision meant, and
that approving evidence is not the same as writing it to a record; the profile
form confirms rather than just clearing itself, which otherwise reads as the
input being discarded.

**The fact table was empty**, so the review queue and every provenance tooltip
had nothing to show — the mechanism that makes an agent-written CRM
trustworthy, invisible. Six agent-derived facts seeded with a deliberate mix:
two applied, showing what a confident agent writes unprompted, and four
proposed, including one weak claim that a reviewer should reject, so the queue
is not a row of obvious approvals. Each carries a score, a band, evidence and
where available a source. Idempotent on subject+field+value; verified over two
runs.

Verified: toast confirmed firing in a real browser on a 393px viewport, 135
unit tests and e2e green, typecheck clean, CSP hash unchanged, 0px horizontal
overflow across 12 routes at both breakpoints.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-13 03:39:00 -07:00
parent 2763531ce4
commit c2c7fb9c19
7 changed files with 242 additions and 20 deletions
+160
View File
@@ -41,6 +41,7 @@ import { createDatabase } from '../client';
import {
accounts,
activities,
facts,
allocations,
capacityCommitments,
capacityRequests,
@@ -660,7 +661,166 @@ async function seedDemo() {
}
}
// -------------------------------------------------- agent-derived facts
//
// Without these the fact-review queue and every provenance tooltip are
// empty, which hides the thing that makes an agent-written CRM trustworthy:
// that each claim carries a score, a band, evidence and a source, and that
// only verified claims apply themselves.
//
// The mix is deliberate. Two `applied` facts show what a confident agent
// writes unprompted; four `proposed` show what waits for a human; one is a
// near-miss that a reviewer should reject, so the queue is not a row of
// obvious approvals.
const factSeeds: {
accountDomain?: string;
contactName?: string;
field: string;
value: string;
score: string;
band: 'verified' | 'probable' | 'possible';
status: 'applied' | 'proposed';
method: string;
sourceUrl?: string;
evidence: Record<string, unknown>;
}[] = [
{
accountDomain: 'coreweave.com',
field: 'supplierType',
value: 'neocloud',
score: '0.960',
band: 'verified',
status: 'applied',
method: 'web_search',
sourceUrl: 'https://www.coreweave.com/',
evidence: {
quote: 'Describes itself as an AI hyperscaler providing GPU cloud infrastructure.',
corroboration: 2,
},
},
{
accountDomain: 'nebius.com',
field: 'jurisdiction',
value: 'European Union',
score: '0.910',
band: 'verified',
status: 'applied',
method: 'web_search',
sourceUrl: 'https://nebius.com/',
evidence: {
quote: 'Operates a datacentre in Finland, inside the EU data-residency perimeter.',
matters: 'Determines eligibility for customers with EU residency requirements.',
},
},
{
accountDomain: 'crusoe.ai',
field: 'certifications',
value: 'SOC 2 Type II',
score: '0.720',
band: 'probable',
status: 'proposed',
method: 'web_search',
sourceUrl: 'https://crusoe.ai/',
evidence: {
quote: 'A trust page references SOC 2, but the report scope and observation window are not stated.',
caution: 'Scope matters — a report can cover only some products.',
},
},
{
accountDomain: 'lambda.ai',
field: 'supplierType',
value: 'neocloud',
score: '0.680',
band: 'probable',
status: 'proposed',
method: 'web_search',
sourceUrl: 'https://lambda.ai/',
evidence: { quote: 'Markets GPU cloud and on-premises clusters.' },
},
{
contactName: 'Dana Whitfield',
field: 'title',
value: 'VP Infrastructure',
score: '0.540',
band: 'possible',
status: 'proposed',
method: 'inference',
evidence: {
reasoning: 'A conference bio lists a VP title; the CRM records Head of Infrastructure.',
conflict: 'Sources disagree, and neither is dated.',
},
},
{
accountDomain: 'runpod.io',
field: 'customerSegment',
value: 'frontier_lab',
score: '0.310',
band: 'possible',
status: 'proposed',
method: 'inference',
evidence: {
reasoning: 'Inferred from a blog post mentioning large training runs.',
warning:
'Weak. This is a supply-side provider, not a frontier lab — a reviewer should reject it.',
},
},
];
let factsAdded = 0;
for (const seed of factSeeds) {
let accountId: string | undefined;
let contactId: string | undefined;
if (seed.accountDomain) {
const [row] = await db
.select({ id: accounts.id })
.from(accounts)
.where(eq(accounts.domain, seed.accountDomain))
.limit(1);
accountId = row?.id;
}
if (seed.contactName) {
const [row] = await db
.select({ id: contacts.id })
.from(contacts)
.where(eq(contacts.fullName, seed.contactName))
.limit(1);
contactId = row?.id;
}
if (!accountId && !contactId) continue;
// Idempotent on the natural key: one claim per subject per field per value.
const [existing] = await db
.select({ id: facts.id })
.from(facts)
.where(
and(
accountId ? eq(facts.accountId, accountId) : eq(facts.contactId, contactId!),
eq(facts.field, seed.field),
eq(facts.value, seed.value),
),
)
.limit(1);
if (existing) continue;
await db.insert(facts).values({
accountId,
contactId,
field: seed.field,
value: seed.value,
score: seed.score,
band: seed.band,
status: seed.status,
method: seed.method,
sourceUrl: seed.sourceUrl,
evidence: seed.evidence,
observedAt: at(-Math.round(Math.random() * 6) - 1),
});
factsAdded += 1;
}
console.log(' 4 capacity commitments, with sites, MSAs and negotiated SLAs');
console.log(` ${factSeeds.length} agent-derived facts (${factsAdded} new) — 2 applied, 4 awaiting review`);
console.log(' 6 demand deals across the pipeline, 5 supply deals');
console.log(' Allocations including one unconverted hold and internal research burn');
console.log('\nEverything is prefixed "DEMO — ". Remove it with: npm run db:demo -- --clear');