diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml
new file mode 100644
index 00000000..731ac0f3
--- /dev/null
+++ b/.github/workflows/cd-dev.yml
@@ -0,0 +1,64 @@
+name: Deploy Kaapi Dev to EC2
+
+on:
+ push:
+ branches:
+ - dev
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ environment: AWS_ENV
+
+ permissions:
+ packages: write
+ contents: read
+ attestations: write
+ id-token: write
+
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v6
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v6
+ with:
+ role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
+ aws-region: ${{ secrets.AWS_REGION }}
+
+ - name: Deploy via SSM
+ id: ssm
+ env:
+ BUILD_DIRECTORY: ${{ secrets.DEV_BUILD_DIRECTORY }}
+ APP_NAME: ${{ secrets.DEV_PM2_APP_NAME }}
+ AWS_REGION: ${{ secrets.AWS_REGION }}
+ INSTANCE_ID: ${{ secrets.EC2_STAGING_INSTANCE_ID }}
+ ROOT_USER: ${{ secrets.USER }}
+ run: |
+ REMOTE_CMD="export HOME=/home/$ROOT_USER && export NVM_DIR="/home/$ROOT_USER/.nvm" && [ -s "\$NVM_DIR/nvm.sh" ] && \. "\$NVM_DIR/nvm.sh" && git config --global --add safe.directory ${BUILD_DIRECTORY} && set -e && cd ${BUILD_DIRECTORY} && git pull origin dev && npm ci && npm run build && sudo -iu ${ROOT_USER} pm2 restart ${APP_NAME}"
+ CMD_ID=$(aws ssm send-command \
+ --instance-ids "$INSTANCE_ID" \
+ --document-name "AWS-RunShellScript" \
+ --parameters commands="[\"$REMOTE_CMD\"]" \
+ --region "$AWS_REGION" \
+ --query 'Command.CommandId' \
+ --output text)
+ echo "cmd_id=$CMD_ID" >> "$GITHUB_OUTPUT"
+
+ - name: Wait for SSM command to finish
+ env:
+ INSTANCE_ID: ${{ secrets.EC2_STAGING_INSTANCE_ID }}
+ CMD_ID: ${{ steps.ssm.outputs.cmd_id }}
+ run: |
+ WAIT_EXIT=0
+ aws ssm wait command-executed \
+ --command-id "$CMD_ID" \
+ --instance-id "$INSTANCE_ID" || WAIT_EXIT=$?
+
+ aws ssm get-command-invocation \
+ --command-id "$CMD_ID" \
+ --instance-id "$INSTANCE_ID" \
+ --query '{Status:Status,Stdout:StandardOutputContent,Stderr:StandardErrorContent}' \
+ --output json
+
+ exit $WAIT_EXIT
diff --git a/app/(main)/assessment/page.tsx b/app/(main)/assessment/page.tsx
new file mode 100644
index 00000000..e8e295bb
--- /dev/null
+++ b/app/(main)/assessment/page.tsx
@@ -0,0 +1,19 @@
+"use client";
+
+import { Suspense } from "react";
+import { Loader } from "@/app/components/ui";
+import PageLayout from "@/app/components/assessment/PageLayout";
+import { useAssessmentWorkflow } from "@/app/hooks/useAssessmentWorkflow";
+
+function PageContent() {
+ const layoutProps = useAssessmentWorkflow();
+ return
{error}
++ Choose a role for each column. +
++ No columns found. +
++ Go back and select a dataset first. +
++ No dataset selected +
++ Select a dataset first from the Datasets tab +
+ ++ {subtitle ?? `${rows.length} rows · ${headers.length} columns`} +
+| + {headers.map((header, i) => ( + | + {header} + | + ))} +
|---|---|
| + {rowIdx + 1} + | + {row.map((cell, cellIdx) => ( +
+
+ {cell || —}
+
+ |
+ ))}
+
+ Are you sure you want to delete +
++ {datasetPendingDelete?.dataset_name}? +
++ This action cannot be undone. +
++ No evaluation runs yet +
++ Submit an assessment from the Config tab to get started +
++ No {statusFilter} runs +
++ No evaluation runs with status "{statusFilter}" +
+
+ {placeholder}
+
+ )}
+
+ {/* Highlighted layer */}
+
+
+ {/* Editable layer */}
+ {description}
+ ) : null} +
+ Available columns — type{" "}
+
+ @
+ {" "}
+ in formula to reference
+
+ No computed columns. Add one to create formulas like{" "}
+
+ @Novelty_score + @Feasibility_score
+
+ .
+
+ No filters — all rows included. +
+ )} + ++ No sort rules — original row order preserved. +
+ )} + ++ Optional. Define computed columns, filters, and sort rules applied + to results at export time. This step is optional — click Next to + skip. +
+
+ Available columns — type{" "}
+ @{" "}
+ in formulas to reference
+
+ @Novelty_score + @Feasibility_score
+
+ + Optional pre-filters run before the LLM batch. Rows that fail Topic + Relevance are excluded from Evaluation and flagged in the export. +
++ Select at least one text or document column. +
+ )} + {trColumns.length > 0 && ( +
+ Export will include:{" "}
+ {trColumns
+ .map((c) => (
+
+ topic_relevance_{c}
+
+ ))
+ .reduce
+ {trAttachmentColumns.length > 0 + ? "Selected documents are sent to the model — Topic Relevance is judged on text and these documents." + : "No documents selected — Topic Relevance uses text columns only."} +
++ Select at least one column. +
+ )} ++ Write the task on the left. Tune behavior and output on the right. +
++ Verify your evaluation configuration before submitting. +
+{subtitle}
+ )} ++ Upload a CSV file for evaluation +
++ {uploadedFile.name} +
++ {(uploadedFile.size / 1024).toFixed(1)} KB +
++ Drop file here, or click to browse +
++ CSV or Excel (.xlsx, .xls) +
++ Use an existing dataset from the list, or create a new dataset + from the form on the right. +
++ No datasets yet +
++ Create your first dataset using the form on the right +
++ Define the structure of the AI response. Use the visual editor or switch + to JSON. +
+ +