Skip to content
Open
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
45 changes: 43 additions & 2 deletions .github/workflows/vercel-deploy-hook-backup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
ping-vercel-deploy-hook:
name: Ping Vercel Deploy Hook
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 15
steps:
- name: Sanity-check secret presence
run: |
Expand All @@ -50,6 +50,22 @@ jobs:
fi
echo "secret-present=true"

# Why this wait: GitHub's main branch HEAD takes a few seconds to
# propagate to Vercel's git-mirror. If we fire the deploy hook
# immediately after a merge, Vercel can clone an OLDER commit and
# ship a build that doesn't contain the just-merged code — exactly
# what bit us on commit 5a4d690 (PR #204): a deploy completed at
# 08:13:43 but built without the merged source, causing 15 minutes
# of stale serving until a manual re-trigger. Native GitHub→Vercel
# webhook integration usually handles this race because it passes
# the commit SHA explicitly; deploy hook URLs do not, so we wait.
- name: Wait for git mirror propagation (race fix)
if: github.event_name == 'push'
run: |
echo "Waiting 60s before firing deploy hook so Vercel's git mirror catches up to GitHub HEAD..."
sleep 60
echo "Done waiting."

- name: Trigger Vercel deploy hook
env:
DEPLOY_HOOK_URL: ${{ secrets.VERCEL_DEPLOY_HOOK_URL }}
Expand All @@ -67,11 +83,36 @@ jobs:
exit 1
fi

# Tier-A live verification per .claude/rules/live_dom_verification.md:
# poll the production URL until the entry-bundle hash changes from
# whatever was previously served. Times out at ~7 minutes
# (35 polls × 12s). Non-blocking — if Vercel takes longer, we still
# fail loudly so engineers know to check.
- name: Verify new bundle is live (Tier A)
if: github.event_name == 'push'
env:
PROD_URL: https://www.nodebenchai.com
run: |
echo "Capturing baseline bundle hash before deploy completes..."
baseline=$(curl -fsS "$PROD_URL/?nc=$RANDOM-$(date +%s)" 2>/dev/null | grep -oE 'assets/index-[A-Za-z0-9_-]+\.js' | head -1 || echo "none")
echo "Baseline: $baseline"
for i in $(seq 1 35); do
sleep 12
current=$(curl -fsS "$PROD_URL/?nc=$RANDOM-$(date +%s)" 2>/dev/null | grep -oE 'assets/index-[A-Za-z0-9_-]+\.js' | head -1 || echo "none")
if [ "$current" != "$baseline" ] && [ "$current" != "none" ]; then
echo "::notice::Live bundle rotated to $current after ~$((i*12))s"
echo "live-verified=true" >> "$GITHUB_OUTPUT" 2>/dev/null || true
exit 0
fi
echo "Poll $i/35: still $current"
done
echo "::warning::Live bundle did not rotate within 7 minutes. Vercel deploy may still be in progress, or edge cache is stuck. Check https://vercel.com/hshum2018-gmailcoms-projects/nodebench-ai"

- name: Summary
if: success()
run: |
echo "## Vercel deploy hook fired" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Triggered for commit \`${{ github.sha }}\` on \`main\`." >> $GITHUB_STEP_SUMMARY
echo "Triggered for commit \`${{ github.sha }}\` on \`main\` after 60s mirror-propagation wait." >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Vercel deduplicates by commit SHA, so this is a no-op if the native push integration also fired. If the native integration is broken, this is the only thing that ships your code." >> $GITHUB_STEP_SUMMARY
10 changes: 10 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,13 @@ import type * as domains_evaluation_validators from "../domains/evaluation/valid
import type * as domains_evaluation_workbenchQueries from "../domains/evaluation/workbenchQueries.js";
import type * as domains_financialOperator_attFixture from "../domains/financialOperator/attFixture.js";
import type * as domains_financialOperator_extractors from "../domains/financialOperator/extractors.js";
import type * as domains_financialOperator_fixtures_covenantFixture from "../domains/financialOperator/fixtures/covenantFixture.js";
import type * as domains_financialOperator_fixtures_crmFixture from "../domains/financialOperator/fixtures/crmFixture.js";
import type * as domains_financialOperator_fixtures_varianceFixture from "../domains/financialOperator/fixtures/varianceFixture.js";
import type * as domains_financialOperator_index from "../domains/financialOperator/index.js";
import type * as domains_financialOperator_orchestrator from "../domains/financialOperator/orchestrator.js";
import type * as domains_financialOperator_orchestratorExamples from "../domains/financialOperator/orchestratorExamples.js";
import type * as domains_financialOperator_realExtractors from "../domains/financialOperator/realExtractors.js";
import type * as domains_financialOperator_runOps from "../domains/financialOperator/runOps.js";
import type * as domains_financialOperator_sandbox from "../domains/financialOperator/sandbox.js";
import type * as domains_financialOperator_types from "../domains/financialOperator/types.js";
Expand Down Expand Up @@ -1994,8 +1999,13 @@ declare const fullApi: ApiFromModules<{
"domains/evaluation/workbenchQueries": typeof domains_evaluation_workbenchQueries;
"domains/financialOperator/attFixture": typeof domains_financialOperator_attFixture;
"domains/financialOperator/extractors": typeof domains_financialOperator_extractors;
"domains/financialOperator/fixtures/covenantFixture": typeof domains_financialOperator_fixtures_covenantFixture;
"domains/financialOperator/fixtures/crmFixture": typeof domains_financialOperator_fixtures_crmFixture;
"domains/financialOperator/fixtures/varianceFixture": typeof domains_financialOperator_fixtures_varianceFixture;
"domains/financialOperator/index": typeof domains_financialOperator_index;
"domains/financialOperator/orchestrator": typeof domains_financialOperator_orchestrator;
"domains/financialOperator/orchestratorExamples": typeof domains_financialOperator_orchestratorExamples;
"domains/financialOperator/realExtractors": typeof domains_financialOperator_realExtractors;
"domains/financialOperator/runOps": typeof domains_financialOperator_runOps;
"domains/financialOperator/sandbox": typeof domains_financialOperator_sandbox;
"domains/financialOperator/types": typeof domains_financialOperator_types;
Expand Down
74 changes: 74 additions & 0 deletions convex/domains/financialOperator/fixtures/covenantFixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Credit-agreement covenant compliance fixture — Example C.
*
* Demo numbers chosen to land inside the leverage covenant (3.55x vs
* 4.25x cap) so the deterministic sandbox produces a clean "compliant"
* verdict. Real implementations should hit a covenant-extractor backed
* by an LLM section reader.
*/

export const COVENANT_FIXTURE = {
meta: {
borrower: "Demo Borrower Inc.",
creditAgreementFile: "credit_agreement.pdf",
financialsFile: "q4_financials.xlsx",
},

sections: [
{
page: 42,
label: "Financial Covenants",
match: "Maximum Total Net Leverage Ratio",
},
{
page: 87,
label: "Definitions — Consolidated EBITDA",
match: "Consolidated EBITDA",
},
{
page: 91,
label: "Definitions — Total Net Debt",
match: "Total Net Debt",
},
],

covenant: {
name: "Maximum Total Net Leverage Ratio",
threshold: 4.25,
ratioType: "Total Net Debt / Consolidated EBITDA",
numeratorDefinition: "Total Net Debt = total debt - unrestricted cash",
denominatorDefinition: "Consolidated EBITDA (with permitted add-backs)",
},

// Inputs in dollars (full units, not millions, so the formatter shows $.XM)
inputs: {
totalDebt: {
value: 840_000_000,
sourceRef: "Q4 Financials — Debt Schedule!B14",
confidence: 0.96,
},
cash: {
value: 95_000_000,
sourceRef: "Q4 Financials — Balance Sheet!B21",
confidence: 0.98,
},
adjustedEBITDA: {
value: 210_000_000,
sourceRef: "Q4 Financials — Adjusted EBITDA!B37",
confidence: 0.88,
},
},

excerpts: [
{
sourceRef: "Credit Agreement p.42",
excerpt:
"Borrower shall not permit the Total Net Leverage Ratio at the end of any fiscal quarter to exceed 4.25 to 1.00.",
},
{
sourceRef: "Credit Agreement p.87",
excerpt:
"\"Consolidated EBITDA\" means, for any period, the sum of net income plus interest expense, taxes, depreciation, amortization, and certain permitted non-recurring add-backs.",
},
],
} as const;
65 changes: 65 additions & 0 deletions convex/domains/financialOperator/fixtures/crmFixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* CRM cleanup fixture — Example B in the operator-console spec.
*
* Stand-in for a real spreadsheet+PDF dedup pipeline. Stable, citable,
* deterministic. Replace `extractCrmInputs` with a real reader once the
* spreadsheet/PDF parsing surface is wired.
*/

export const CRM_FIXTURE = {
meta: {
files: [
{ name: "prospects.xlsx", kind: "xlsx" },
{ name: "investor_packet_1.pdf", kind: "pdf" },
{ name: "investor_packet_2.pdf", kind: "pdf" },
{ name: "investor_packet_3.pdf", kind: "pdf" },
],
spreadsheetSheets: ["Raw Leads", "Notes", "Duplicates"],
pdfPages: 86,
},

profile: {
columns: ["Company", "Website", "Notes", "Partner", "Status"],
rowCount: 387,
missingFields: ["Sector", "HQ", "Last Round", "Source"],
},

entityExtraction: {
companiesFound: 142,
fundingEventsFound: 37,
locationsFound: 91,
},

dedup: {
originalRows: 387,
dedupedRows: 312,
mergedExamples: [
{
canonical: "Acme Bio",
merged: ["AcmeBio", "Acme Bio Inc.", "acmebio.com"],
},
{
canonical: "Northstar Robotics",
merged: ["Northstar Robotics LLC", "northstar-robotics.io"],
},
],
},

enrichment: {
recordsUpdated: 241,
lowConfidenceRecords: 31,
unresolvedRecords: 40,
sampleEnriched: [
{ company: "Acme Bio", sector: "Biotech", hq: "Boston, MA", lastRound: "Series B" },
{ company: "Northstar Robotics", sector: "Industrial AI", hq: "Pittsburgh, PA", lastRound: "Series A" },
{ company: "HelioGrid", sector: "Energy", hq: "Austin, TX", lastRound: "Seed" },
],
},

csvValidation: {
schema: ["Company", "Website", "Sector", "HQ", "Last Round", "Source", "Confidence", "Owner"],
validRows: 295,
warningRows: 17,
failedRows: 0,
},
} as const;
72 changes: 72 additions & 0 deletions convex/domains/financialOperator/fixtures/varianceFixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Monthly variance-analysis fixture — Example D.
*
* Includes a diverse top/bottom mix so the variance sandbox renders
* both signed-positive and signed-negative formatting paths.
*/

export interface VarianceLine {
account: string;
category: "revenue" | "cost_of_revenue" | "opex" | "infrastructure" | "other";
actual: number;
budget: number;
driverNote?: string;
}

export const VARIANCE_FIXTURE = {
period: "March 2026",
files: [
{ name: "march_actuals.xlsx", kind: "xlsx" },
{ name: "fy_budget.xlsx", kind: "xlsx" },
],
alignment: {
matchedAccounts: 124,
unmatchedActuals: 3,
unmatchedBudget: 5,
},
// Lines in dollars; formatter divides by 1M for display.
lines: [
{
account: "Subscription Revenue",
category: "revenue",
actual: 4_200_000,
budget: 3_700_000,
driverNote: "Enterprise renewals closed earlier than budgeted.",
},
{
account: "Services Revenue",
category: "revenue",
actual: 850_000,
budget: 900_000,
driverNote: "One implementation slipped to April.",
},
{
account: "Cloud Infrastructure",
category: "infrastructure",
actual: 980_000,
budget: 720_000,
driverNote: "GPU inference costs rose with higher agent run volume.",
},
{
account: "Sales Headcount",
category: "opex",
actual: 1_100_000,
budget: 1_150_000,
driverNote: "Two requisitions delayed by one month.",
},
{
account: "Engineering Headcount",
category: "opex",
actual: 1_400_000,
budget: 1_350_000,
driverNote: "On plan; small-bonus accrual.",
},
{
account: "Marketing",
category: "opex",
actual: 320_000,
budget: 400_000,
driverNote: "Demand-gen campaign delayed.",
},
] satisfies VarianceLine[],
} as const;
2 changes: 2 additions & 0 deletions convex/domains/financialOperator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@

export * as runOps from "./runOps";
export * as orchestrator from "./orchestrator";
export * as orchestratorExamples from "./orchestratorExamples";
export * as realExtractors from "./realExtractors";
Loading
Loading