Skip to content

Prototype dataset builder harness#8

Closed
giaphutran12 wants to merge 1 commit into
mainfrom
codex/bigset-agent-harness-autopilot
Closed

Prototype dataset builder harness#8
giaphutran12 wants to merge 1 commit into
mainfrom
codex/bigset-agent-harness-autopilot

Conversation

@giaphutran12

Copy link
Copy Markdown
Collaborator

Closes #4

Summary

  • add backend dataset-builder planner, harness stages, TinyFish goal/schema generation, and optional OpenRouter refinement
  • add authenticated plan route plus dataset/dataset_run metadata tables
  • document prototype command, API contract, MVP decisions, and next tickets

Verification

  • npm run build
  • npm run builder:plan -- "car insurance quotes for a 2021 Honda Civic in Menlo Park"
  • npm run builder:plan -- "latest blog posts from exa and perplexity" --use-openrouter
  • python3 .codex-autorunner/bin/lint_tickets.py
  • car ticket-flow preflight --repo /Users/edwardtran/side\ projects/tinyfish-stuff/bigset --no-json

@giaphutran12 giaphutran12 self-assigned this May 16, 2026
@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5bbcf459-4cb6-48b6-a838-0b9cac31ba6e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces a dataset-builder agent harness MVP that converts user requests into executable dataset build plans. The implementation spans type definitions, deterministic planning with optional LLM refinement via OpenRouter, agent harness stage orchestration for TinyFish, a new HTTP API endpoint, database schema for persistence, and supporting infrastructure. The deterministic planner infers dataset name, schema columns, clarifying questions, and source strategy from request keywords and provided context; optional OpenRouter refinement enhances the draft plan when an API key is available. Agent harness generation produces ordered task stages and generates TinyFish agent goals/output schemas. A CLI prototype and HTTP route enable local testing and API-driven planning.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant API as POST /api/dataset-builder/plan
  participant Planner as createDatasetBuildPlan
  participant Deterministic as Deterministic Planner
  participant OpenRouter as OpenRouter Client
  participant Harness as Agent Harness
  participant DB as Database
  
  User->>API: Send dataset build request
  API->>API: Authenticate user session
  API->>API: Validate & parse request body
  API->>Planner: Create plan (with optional OpenRouter client)
  Planner->>Deterministic: Generate draft plan
  Deterministic->>Deterministic: Infer schema, columns, questions, strategy
  Deterministic-->>Planner: Return draft plan
  alt planningMode = "openrouter" AND client provided
    Planner->>OpenRouter: Refine draft plan via LLM
    OpenRouter-->>Planner: Return refined plan or throw
  else Refinement fails or unavailable
    Planner-->>Planner: Append warning, use draft plan
  end
  Planner-->>API: Resolved plan
  API->>Harness: Generate TinyFish goal & output schema
  Harness-->>API: Goal string & JSON schema
  API->>DB: (Future) Persist plan
  API-->>User: Return planId, plan, goal, schema
Loading

Possibly related PRs

  • tinyfish-io/bigset#2: Introduced foundational backend/src/env.ts with authentication and core environment configuration that this PR directly extends to add OpenRouter and TinyFish integration variables.

Suggested reviewers

  • manav-tf
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Prototype dataset builder harness' accurately summarizes the main change, which introduces the dataset builder harness infrastructure with planner, agent-harness, and route components.
Description check ✅ Passed The description is directly related to the changeset, covering backend planner, harness stages, TinyFish integration, OpenRouter refinement, authenticated route, metadata tables, and documentation.
Linked Issues check ✅ Passed The PR successfully implements all core MVP objectives from issue #4: deterministic planner [types.ts, planner.ts], OpenRouter refinement [openrouter.ts], harness stage generation [agent-harness.ts], TinyFish CLI adapter [tinyfish-cli.ts], authenticated API route [dataset-builder.ts], and metadata tables [schema.ts]. Clarifying questions, schema validation, and fallback behavior are all addressed.
Out of Scope Changes check ✅ Passed All changes align with the MVP scope: environment configuration updates support the new integrations, .gitignore additions are infrastructure-related, and documentation covers prototype usage and next steps without adding unrelated functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/bigset-agent-harness-autopilot

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@giaphutran12

Copy link
Copy Markdown
Collaborator Author

Ticket flow is complete locally and CAR marked TICKET-001 done. Build/security checks passed. PR is blocked only on repo rule requiring one write-access approval; requested @pranavjana for review.

@giaphutran12 giaphutran12 requested a review from pranavjana May 16, 2026 12:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (1)
backend/src/schema.ts (1)

86-87: ⚡ Quick win

Add DB defaults for audit timestamps

Line 86/87 and Line 111/112 are non-null audit fields without defaults, which makes inserts brittle for upcoming persistence code paths.

Suggested change
-    createdAt: timestamp("created_at").notNull(),
-    updatedAt: timestamp("updated_at").notNull(),
+    createdAt: timestamp("created_at").notNull().defaultNow(),
+    updatedAt: timestamp("updated_at").notNull().defaultNow(),
@@
-    createdAt: timestamp("created_at").notNull(),
-    updatedAt: timestamp("updated_at").notNull(),
+    createdAt: timestamp("created_at").notNull().defaultNow(),
+    updatedAt: timestamp("updated_at").notNull().defaultNow(),

Also applies to: 111-112

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/schema.ts` around lines 86 - 87, The createdAt and updatedAt
timestamp columns (the timestamp("created_at") and timestamp("updated_at")
fields) are non-null but lack DB defaults; update their column definitions to
supply a database default of the current time (e.g., DEFAULT CURRENT_TIMESTAMP /
use the ORM's defaultNow helper) for both createdAt and updatedAt, and for the
later repeated audit fields (the other createdAt/updatedAt declarations) do the
same; for updatedAt also add an automatic update behavior (ON UPDATE
CURRENT_TIMESTAMP or the ORM equivalent) or ensure the application updates it on
write.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/dataset-builder/agent-harness.ts`:
- Around line 96-108: The JSON schema for rows only requires
plan.schema.identityColumnName and ignores other columns marked as isRequired;
update the construction of the rows schema in agent-harness.ts so the required
array includes plan.schema.identityColumnName plus every column name from
plan.schema.columns (or equivalent) where isRequired === true (deduplicate if
necessary) before assigning it to the rows.required field; ensure you keep
additionalProperties: false and the existing rowProperties mapping intact so
validation enforces those required columns (e.g., update the code that sets
required: [plan.schema.identityColumnName] to compute and use a merged
requiredFields array).

In `@backend/src/dataset-builder/run-prototype.ts`:
- Around line 27-31: The code silently yields an undefined openRouterClient when
shouldUseOpenRouter is true but process.env.OPENROUTER_API_KEY is missing;
update the logic around shouldUseOpenRouter and createOpenRouterPlannerClient to
explicitly validate the API key: if shouldUseOpenRouter is true and
process.env.OPENROUTER_API_KEY is falsy, throw or exit with a clear error
message (e.g., "OPENROUTER_API_KEY required when --use-openrouter is set"),
otherwise pass the API key into createOpenRouterPlannerClient and assign to
openRouterClient; reference shouldUseOpenRouter, process.env.OPENROUTER_API_KEY,
createOpenRouterPlannerClient and openRouterClient when making the change.

In `@backend/src/dataset-builder/tinyfish-cli.ts`:
- Around line 53-60: The runTinyFishJson function should guard against hung
external calls and provide stderr when JSON parsing fails: when calling
execFileAsync in runTinyFishJson, include a 30_000 ms timeout option (e.g., add
timeout: 30_000 alongside env and maxBuffer) and destructure stderr from the
result; then wrap JSON.parse(stdout) in a try-catch and, on failure, throw a new
Error that includes the parse error message plus the captured stderr (and stdout
context if helpful) so callers get diagnostic details; keep the function
signature and return type (JSON parsed as unknown).

In `@backend/src/dataset-builder/types.ts`:
- Around line 71-77: The DatasetRunCell.value type currently excludes
objects/arrays causing a mismatch with DatasetColumnKind "json"; update the
DatasetRunCell interface (specifically the value property) to accept JSON values
(e.g., union with unknown | Record<string, unknown> | unknown[] or a JSON type
alias) so object and array cells are allowed, and ensure downstream places that
consume DatasetRunCell.value are adjusted to handle the broader JSON type (look
for usages of DatasetRunCell.value to update any strict string/number/boolean
assumptions).

In `@backend/src/routes/dataset-builder.ts`:
- Around line 92-100: The code currently defaults planningMode to "openrouter",
causing userRequest and providedInputs to be sent to a third party by default;
change the return so planningMode is not defaulted to "openrouter" (use
planningMode as-is or undefined) and only enable the openrouter behavior when
planningMode === "openrouter" is explicitly provided by the caller; update the
return object in the function that builds the response (the block returning {
ok: true, value: { userRequest, updateCadence, planningMode: ... ,
providedInputs: parseStringRecord(...), preferredColumns: ... } }) to stop
applying the nullish coalescing default for planningMode and ensure any
downstream code only sends userRequest/providedInputs when planningMode is
explicitly "openrouter".
- Around line 76-90: The current guards use truthy checks on body.updateCadence
and body.planningMode which let empty strings slip past validation; update the
checks around parseUpdateCadence and parsePlanningMode to explicitly detect
presence (e.g., check body.updateCadence !== undefined && body.updateCadence !==
null and if it's a string ensure length > 0) before calling the parser and
returning the error when the parser returns falsy; reference the symbols
updateCadence, parseUpdateCadence, planningMode, parsePlanningMode,
body.updateCadence, and body.planningMode when making the change.

In `@backend/src/schema.ts`:
- Around line 77-83: The status and updateCadence columns currently use
TypeScript-only unions (.$type<...>()) so invalid strings can be persisted;
replace those with pgEnum-backed types by defining pgEnum enums for
DatasetUpdateCadence and the run/dataset status union, then change the column
definitions (updateCadence and status) to use pgEnum(...) instead of text(...).
Ensure you create the enum declarations (e.g., DatasetUpdateCadence enum and
DatasetStatus/RunStatus enum) and reference them in the schema column
definitions so Postgres enforces allowed values at the DB layer.

In `@notion-docs.md`:
- Around line 1-131: This document (# BigSet Technical Specs & Goals ⛓ /
headings Timeline, Specifics, Flow A/B/C) contains internal business strategy,
team member PII, partnership details, private Notion and Discord links, and
other non-public info — remove all sensitive/internal content from
notion-docs.md and replace with a sanitized public README or a short
public-facing project summary; move the removed content (team names/locations,
marketing goals, co-marketing/convex notes, private Notion/Discord links,
internal references like "UXLabs"/"Alguna") into a private Notion/wiki or
private repo and update the public file to reference that private doc only (or
omit links), and ensure the commit does not expose secrets or internal docs
(consider rewriting the commit history if this was already pushed).

---

Nitpick comments:
In `@backend/src/schema.ts`:
- Around line 86-87: The createdAt and updatedAt timestamp columns (the
timestamp("created_at") and timestamp("updated_at") fields) are non-null but
lack DB defaults; update their column definitions to supply a database default
of the current time (e.g., DEFAULT CURRENT_TIMESTAMP / use the ORM's defaultNow
helper) for both createdAt and updatedAt, and for the later repeated audit
fields (the other createdAt/updatedAt declarations) do the same; for updatedAt
also add an automatic update behavior (ON UPDATE CURRENT_TIMESTAMP or the ORM
equivalent) or ensure the application updates it on write.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0df79e24-cb25-4442-8404-d9e1d9dac28e

📥 Commits

Reviewing files that changed from the base of the PR and between 4552227 and 288c710.

⛔ Files ignored due to path filters (2)
  • image-1.png is excluded by !**/*.png
  • image.png is excluded by !**/*.png
📒 Files selected for processing (16)
  • .gitignore
  • backend/.env.example
  • backend/drizzle.config.ts
  • backend/package.json
  • backend/src/dataset-builder/agent-harness.ts
  • backend/src/dataset-builder/openrouter.ts
  • backend/src/dataset-builder/planner.ts
  • backend/src/dataset-builder/run-prototype.ts
  • backend/src/dataset-builder/tinyfish-cli.ts
  • backend/src/dataset-builder/types.ts
  • backend/src/env.ts
  • backend/src/index.ts
  • backend/src/routes/dataset-builder.ts
  • backend/src/schema.ts
  • docs/dataset-builder-agent-harness.md
  • notion-docs.md

Comment on lines +96 to +108
return {
type: "object",
additionalProperties: false,
required: ["rows", "sourceUrls", "validationIssues"],
properties: {
rows: {
type: "array",
items: {
type: "object",
additionalProperties: false,
required: [plan.schema.identityColumnName],
properties: rowProperties,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Output schema ignores isRequired columns.

Rows currently require only the identity field, so required columns (for example source_url) can be omitted and still pass schema validation.

Proposed fix
 export function createTinyFishAgentOutputSchema(plan: DatasetBuildPlan) {
   const rowProperties = Object.fromEntries(
     plan.schema.columns.map((column) => [column.name, jsonSchemaForColumn(column)])
   );
+  const requiredRowColumns = Array.from(
+    new Set([
+      plan.schema.identityColumnName,
+      ...plan.schema.columns
+        .filter((column) => column.isRequired)
+        .map((column) => column.name),
+    ])
+  );
 
   return {
@@
       rows: {
         type: "array",
         items: {
           type: "object",
           additionalProperties: false,
-          required: [plan.schema.identityColumnName],
+          required: requiredRowColumns,
           properties: rowProperties,
         },
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
type: "object",
additionalProperties: false,
required: ["rows", "sourceUrls", "validationIssues"],
properties: {
rows: {
type: "array",
items: {
type: "object",
additionalProperties: false,
required: [plan.schema.identityColumnName],
properties: rowProperties,
},
export function createTinyFishAgentOutputSchema(plan: DatasetBuildPlan) {
const rowProperties = Object.fromEntries(
plan.schema.columns.map((column) => [column.name, jsonSchemaForColumn(column)])
);
const requiredRowColumns = Array.from(
new Set([
plan.schema.identityColumnName,
...plan.schema.columns
.filter((column) => column.isRequired)
.map((column) => column.name),
])
);
return {
type: "object",
additionalProperties: false,
required: ["rows", "sourceUrls", "validationIssues"],
properties: {
rows: {
type: "array",
items: {
type: "object",
additionalProperties: false,
required: requiredRowColumns,
properties: rowProperties,
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/dataset-builder/agent-harness.ts` around lines 96 - 108, The JSON
schema for rows only requires plan.schema.identityColumnName and ignores other
columns marked as isRequired; update the construction of the rows schema in
agent-harness.ts so the required array includes plan.schema.identityColumnName
plus every column name from plan.schema.columns (or equivalent) where isRequired
=== true (deduplicate if necessary) before assigning it to the rows.required
field; ensure you keep additionalProperties: false and the existing
rowProperties mapping intact so validation enforces those required columns
(e.g., update the code that sets required: [plan.schema.identityColumnName] to
compute and use a merged requiredFields array).

Comment on lines +27 to +31
openRouterClient: shouldUseOpenRouter
? createOpenRouterPlannerClient({
apiKey: process.env.OPENROUTER_API_KEY,
model: process.env.OPENROUTER_MODEL || "openai/gpt-4.1-mini",
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether missing apiKey is already handled in client construction
rg -n -C3 'createOpenRouterPlannerClient|apiKey|OPENROUTER_API_KEY' \
  backend/src/dataset-builder/openrouter.ts \
  backend/src/dataset-builder/run-prototype.ts

Repository: tinyfish-io/bigset

Length of output: 3929


Add explicit validation when --use-openrouter flag is used without API key

When --use-openrouter is set without OPENROUTER_API_KEY, the OpenRouter client silently returns undefined instead of alerting the user. This creates a confusing situation where an explicit flag request fails silently.

   {
     openRouterClient: shouldUseOpenRouter
-      ? createOpenRouterPlannerClient({
-          apiKey: process.env.OPENROUTER_API_KEY,
+      ? createOpenRouterPlannerClient({
+          apiKey: (() => {
+            const key = process.env.OPENROUTER_API_KEY;
+            if (!key) {
+              throw new Error(
+                "OPENROUTER_API_KEY is required when using --use-openrouter"
+              );
+            }
+            return key;
+          })(),
           model: process.env.OPENROUTER_MODEL || "openai/gpt-4.1-mini",
         })
       : undefined,
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
openRouterClient: shouldUseOpenRouter
? createOpenRouterPlannerClient({
apiKey: process.env.OPENROUTER_API_KEY,
model: process.env.OPENROUTER_MODEL || "openai/gpt-4.1-mini",
})
openRouterClient: shouldUseOpenRouter
? createOpenRouterPlannerClient({
apiKey: (() => {
const key = process.env.OPENROUTER_API_KEY;
if (!key) {
throw new Error(
"OPENROUTER_API_KEY is required when using --use-openrouter"
);
}
return key;
})(),
model: process.env.OPENROUTER_MODEL || "openai/gpt-4.1-mini",
})
: undefined,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/dataset-builder/run-prototype.ts` around lines 27 - 31, The code
silently yields an undefined openRouterClient when shouldUseOpenRouter is true
but process.env.OPENROUTER_API_KEY is missing; update the logic around
shouldUseOpenRouter and createOpenRouterPlannerClient to explicitly validate the
API key: if shouldUseOpenRouter is true and process.env.OPENROUTER_API_KEY is
falsy, throw or exit with a clear error message (e.g., "OPENROUTER_API_KEY
required when --use-openrouter is set"), otherwise pass the API key into
createOpenRouterPlannerClient and assign to openRouterClient; reference
shouldUseOpenRouter, process.env.OPENROUTER_API_KEY,
createOpenRouterPlannerClient and openRouterClient when making the change.

Comment on lines +53 to +60
async function runTinyFishJson(args: string[]) {
const { stdout } = await execFileAsync("tinyfish", args, {
env: process.env,
maxBuffer: 1024 * 1024 * 10,
});

return JSON.parse(stdout) as unknown;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current invocation safeguards around tinyfish process calls
rg -n -C3 'execFileAsync\(|maxBuffer|timeout|JSON.parse\(' backend/src/dataset-builder/tinyfish-cli.ts

Repository: tinyfish-io/bigset

Length of output: 309


🏁 Script executed:

# Check execFileAsync import and file context
head -n 60 backend/src/dataset-builder/tinyfish-cli.ts

Repository: tinyfish-io/bigset

Length of output: 1511


🏁 Script executed:

# Search for timeout usage patterns in similar execFile calls
rg -n 'timeout.*[0-9]' backend/src/ --type ts | head -20

Repository: tinyfish-io/bigset

Length of output: 178


🏁 Script executed:

# Check if execFileAsync is a Node.js built-in or custom function
rg -n 'execFileAsync|execFile' backend/src/dataset-builder/tinyfish-cli.ts -B5 -A1

Repository: tinyfish-io/bigset

Length of output: 395


🏁 Script executed:

# Verify coding guideline compliance for ESM imports in this file
head -n 30 backend/src/dataset-builder/tinyfish-cli.ts | grep -E '^import|^from'

Repository: tinyfish-io/bigset

Length of output: 148


Harden TinyFish process execution with timeout and error diagnostics

Line 54 executes an external binary without a timeout, allowing a hung CLI run to block indefinitely. Additionally, line 59 discards stderr context when JSON parsing fails, hampering incident triage.

Add a 30-second timeout to the execFileAsync call and wrap JSON.parse in a try-catch that includes stderr output in the error message.

 async function runTinyFishJson(args: string[]) {
-  const { stdout } = await execFileAsync("tinyfish", args, {
+  const { stdout, stderr } = await execFileAsync("tinyfish", args, {
     env: process.env,
     maxBuffer: 1024 * 1024 * 10,
+    timeout: 30_000,
   });
 
-  return JSON.parse(stdout) as unknown;
+  try {
+    return JSON.parse(stdout) as unknown;
+  } catch (error) {
+    throw new Error(
+      `tinyfish returned non-JSON output. stderr=${stderr?.slice(0, 500) ?? ""}`,
+      { cause: error }
+    );
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/dataset-builder/tinyfish-cli.ts` around lines 53 - 60, The
runTinyFishJson function should guard against hung external calls and provide
stderr when JSON parsing fails: when calling execFileAsync in runTinyFishJson,
include a 30_000 ms timeout option (e.g., add timeout: 30_000 alongside env and
maxBuffer) and destructure stderr from the result; then wrap JSON.parse(stdout)
in a try-catch and, on failure, throw a new Error that includes the parse error
message plus the captured stderr (and stdout context if helpful) so callers get
diagnostic details; keep the function signature and return type (JSON parsed as
unknown).

Comment on lines +71 to +77
export interface DatasetRunCell {
columnName: string;
value: string | number | boolean | null;
sourceUrl?: string;
confidenceScore: number;
validationStatus: "valid" | "missing" | "needs_review";
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Allow JSON-valued cells in DatasetRunCell.

DatasetColumnKind supports "json", and the harness output schema allows object/array values, but DatasetRunCell.value currently rejects them. This creates a contract mismatch for valid JSON columns.

Proposed fix
+export type DatasetCellValue =
+  | string
+  | number
+  | boolean
+  | Record<string, unknown>
+  | unknown[]
+  | null;
+
 export interface DatasetRunCell {
   columnName: string;
-  value: string | number | boolean | null;
+  value: DatasetCellValue;
   sourceUrl?: string;
   confidenceScore: number;
   validationStatus: "valid" | "missing" | "needs_review";
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface DatasetRunCell {
columnName: string;
value: string | number | boolean | null;
sourceUrl?: string;
confidenceScore: number;
validationStatus: "valid" | "missing" | "needs_review";
}
export type DatasetCellValue =
| string
| number
| boolean
| Record<string, unknown>
| unknown[]
| null;
export interface DatasetRunCell {
columnName: string;
value: DatasetCellValue;
sourceUrl?: string;
confidenceScore: number;
validationStatus: "valid" | "missing" | "needs_review";
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/dataset-builder/types.ts` around lines 71 - 77, The
DatasetRunCell.value type currently excludes objects/arrays causing a mismatch
with DatasetColumnKind "json"; update the DatasetRunCell interface (specifically
the value property) to accept JSON values (e.g., union with unknown |
Record<string, unknown> | unknown[] or a JSON type alias) so object and array
cells are allowed, and ensure downstream places that consume
DatasetRunCell.value are adjusted to handle the broader JSON type (look for
usages of DatasetRunCell.value to update any strict string/number/boolean
assumptions).

Comment on lines +76 to +90
const updateCadence = parseUpdateCadence(body.updateCadence);
if (body.updateCadence && !updateCadence) {
return {
ok: false,
error: "`updateCadence` must be manual, hourly, daily, or weekly.",
};
}

const planningMode = parsePlanningMode(body.planningMode);
if (body.planningMode && !planningMode) {
return {
ok: false,
error: "`planningMode` must be deterministic or openrouter.",
};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten enum validation checks for optional fields.

Using truthy guards allows invalid empty-string values to bypass validation and fall through silently.

Proposed fix
   const updateCadence = parseUpdateCadence(body.updateCadence);
-  if (body.updateCadence && !updateCadence) {
+  if (body.updateCadence !== undefined && !updateCadence) {
@@
   const planningMode = parsePlanningMode(body.planningMode);
-  if (body.planningMode && !planningMode) {
+  if (body.planningMode !== undefined && !planningMode) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const updateCadence = parseUpdateCadence(body.updateCadence);
if (body.updateCadence && !updateCadence) {
return {
ok: false,
error: "`updateCadence` must be manual, hourly, daily, or weekly.",
};
}
const planningMode = parsePlanningMode(body.planningMode);
if (body.planningMode && !planningMode) {
return {
ok: false,
error: "`planningMode` must be deterministic or openrouter.",
};
}
const updateCadence = parseUpdateCadence(body.updateCadence);
if (body.updateCadence !== undefined && !updateCadence) {
return {
ok: false,
error: "`updateCadence` must be manual, hourly, daily, or weekly.",
};
}
const planningMode = parsePlanningMode(body.planningMode);
if (body.planningMode !== undefined && !planningMode) {
return {
ok: false,
error: "`planningMode` must be deterministic or openrouter.",
};
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/routes/dataset-builder.ts` around lines 76 - 90, The current
guards use truthy checks on body.updateCadence and body.planningMode which let
empty strings slip past validation; update the checks around parseUpdateCadence
and parsePlanningMode to explicitly detect presence (e.g., check
body.updateCadence !== undefined && body.updateCadence !== null and if it's a
string ensure length > 0) before calling the parser and returning the error when
the parser returns falsy; reference the symbols updateCadence,
parseUpdateCadence, planningMode, parsePlanningMode, body.updateCadence, and
body.planningMode when making the change.

Comment on lines +92 to +100
return {
ok: true,
value: {
userRequest,
updateCadence,
planningMode: planningMode ?? "openrouter",
providedInputs: parseStringRecord(body.providedInputs),
preferredColumns: parseStringArray(body.preferredColumns),
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make OpenRouter refinement opt-in, not default.

Defaulting planningMode to "openrouter" means userRequest and providedInputs are sent to a third party whenever an API key is configured, even if the caller didn’t explicitly ask for that path.

Proposed fix
     value: {
       userRequest,
       updateCadence,
-      planningMode: planningMode ?? "openrouter",
+      planningMode: planningMode ?? "deterministic",
       providedInputs: parseStringRecord(body.providedInputs),
       preferredColumns: parseStringArray(body.preferredColumns),
     },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/routes/dataset-builder.ts` around lines 92 - 100, The code
currently defaults planningMode to "openrouter", causing userRequest and
providedInputs to be sent to a third party by default; change the return so
planningMode is not defaulted to "openrouter" (use planningMode as-is or
undefined) and only enable the openrouter behavior when planningMode ===
"openrouter" is explicitly provided by the caller; update the return object in
the function that builds the response (the block returning { ok: true, value: {
userRequest, updateCadence, planningMode: ... , providedInputs:
parseStringRecord(...), preferredColumns: ... } }) to stop applying the nullish
coalescing default for planningMode and ensure any downstream code only sends
userRequest/providedInputs when planningMode is explicitly "openrouter".

Comment thread backend/src/schema.ts
Comment on lines +77 to +83
updateCadence: text("update_cadence")
.$type<DatasetUpdateCadence>()
.notNull(),
status: text("status")
.$type<"draft" | "needs_input" | "ready" | "running" | "failed">()
.notNull()
.default("draft"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Drizzle ORM v0.45.x, does text(...).$type<...>() enforce runtime/database constraints, or is it TypeScript-only?

💡 Result:

In Drizzle ORM, the.$type<...> method is strictly for TypeScript-only type inference and does not enforce any runtime or database-level constraints [1][2]. It is used to override or specify the TypeScript type for a column, which is particularly useful for handling complex types (like JSON objects), branded types, or custom data structures that Drizzle's default inference might not capture accurately [1][2]. Because it is a compile-time feature, it does not perform any validation on the data being inserted into or retrieved from the database [1][2]. If you require runtime validation or database-level constraints, you should use appropriate mechanisms such as: - Database-level constraints: Use Drizzle's schema definition features for PRIMARY KEY, UNIQUE, NOT NULL, and CHECK constraints, which are enforced by the database engine itself [3][4]. - Runtime validation: For validating data before it reaches the database, you can use schema validation libraries (e.g., Zod, Valibot) or Drizzle's $validator method (where available) to integrate with Standard Schema-compliant validators [5].

Citations:


🏁 Script executed:

# Check the backend package.json for Drizzle ORM version
head -50 backend/package.json | grep -A 5 -B 5 "drizzle"

Repository: tinyfish-io/bigset

Length of output: 623


🏁 Script executed:

# Read the schema.ts file to verify the current state of lines 77-83 and 102-105
cat -n backend/src/schema.ts | head -115 | tail -50

Repository: tinyfish-io/bigset

Length of output: 2094


🏁 Script executed:

# Check what's imported from drizzle-orm/pg-core in the current schema.ts
cat -n backend/src/schema.ts | head -20

Repository: tinyfish-io/bigset

Length of output: 550


🏁 Script executed:

# Find DatasetUpdateCadence type definition
rg "DatasetUpdateCadence" backend/src --type ts -A 5

Repository: tinyfish-io/bigset

Length of output: 4019


🏁 Script executed:

# Check if pgEnum is available in drizzle-orm/pg-core by searching docs or examples
cd backend && npm list drizzle-orm 2>/dev/null | head -5

Repository: tinyfish-io/bigset

Length of output: 123


🏁 Script executed:

# Get the actual type definition for DatasetUpdateCadence
cat -n backend/src/dataset-builder/types.ts | grep -A 2 "DatasetUpdateCadence"

Repository: tinyfish-io/bigset

Length of output: 464


🏁 Script executed:

# Search for all text fields with $type to see if any others need enum enforcement
rg '\$type<' backend/src/schema.ts

Repository: tinyfish-io/bigset

Length of output: 445


Enforce status and cadence values at the database layer

Lines 77 and 102 currently rely on TypeScript-only unions via .$type<...>(). Postgres will accept arbitrary strings, which can leak invalid states into persisted datasets and runs. Use pgEnum to enforce values at the database level.

Suggested change
 import {
   boolean,
   index,
   integer,
   jsonb,
+  pgEnum,
   pgTable,
   text,
   timestamp,
 } from "drizzle-orm/pg-core";

+const datasetCadenceEnum = pgEnum("dataset_cadence", ["manual", "hourly", "daily", "weekly"]);
+const datasetStatusEnum = pgEnum("dataset_status", ["draft", "needs_input", "ready", "running", "failed"]);
+const datasetRunStatusEnum = pgEnum("dataset_run_status", ["queued", "running", "needs_input", "succeeded", "failed"]);
+
 export const dataset = pgTable(
   "dataset",
   {
@@
-    updateCadence: text("update_cadence")
-      .$type<DatasetUpdateCadence>()
-      .notNull(),
-    status: text("status")
-      .$type<"draft" | "needs_input" | "ready" | "running" | "failed">()
-      .notNull()
-      .default("draft"),
+    updateCadence: datasetCadenceEnum("update_cadence").notNull(),
+    status: datasetStatusEnum("status").notNull().default("draft"),
@@
 export const datasetRun = pgTable(
   "dataset_run",
   {
@@
-    status: text("status")
-      .$type<"queued" | "running" | "needs_input" | "succeeded" | "failed">()
-      .notNull()
-      .default("queued"),
+    status: datasetRunStatusEnum("status").notNull().default("queued"),

Also applies to: 102-105

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/schema.ts` around lines 77 - 83, The status and updateCadence
columns currently use TypeScript-only unions (.$type<...>()) so invalid strings
can be persisted; replace those with pgEnum-backed types by defining pgEnum
enums for DatasetUpdateCadence and the run/dataset status union, then change the
column definitions (updateCadence and status) to use pgEnum(...) instead of
text(...). Ensure you create the enum declarations (e.g., DatasetUpdateCadence
enum and DatasetStatus/RunStatus enum) and reference them in the schema column
definitions so Postgres enforces allowed values at the DB layer.

Comment thread notion-docs.md Outdated
@giaphutran12 giaphutran12 marked this pull request as draft May 16, 2026 13:37
@giaphutran12 giaphutran12 removed the request for review from pranavjana May 16, 2026 13:37
@giaphutran12 giaphutran12 force-pushed the codex/bigset-agent-harness-autopilot branch from 288c710 to c71028d Compare May 16, 2026 13:50
@giaphutran12 giaphutran12 deleted the codex/bigset-agent-harness-autopilot branch May 16, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prototype dataset builder agent harness

1 participant