Skip to content

Conversation

@dmfilipenko
Copy link

Problem

Templates table schema was missing the created_at column that the frontend TinyBase store expected. This caused templates to fail loading from SQLite into TinyBase, making user-created templates invisible in the UI dropdown.

Schema Mismatch:

  • Backend SQL: Missing created_at column
  • Backend Rust: Missing created_at field in Template struct
  • Frontend TypeScript: Expected created_at field in TinyBase schema

Solution

  1. Created migration templates_migration_2.sql to add created_at column with auto-populated default value
  2. Updated Template struct to include created_at: String field
  3. Updated from_row() to read created_at from column 7
  4. Updated upsert_template() to insert/update created_at
  5. Registered new migration in MIGRATIONS array

Impact

  • New templates will have created_at automatically set
  • Existing templates get created_at via migration
  • TinyBase can now successfully load templates from SQLite
  • Templates appear correctly in UI dropdown

🤖 Generated with Claude Code

@coderabbitai
Copy link

coderabbitai bot commented Oct 26, 2025

📝 Walkthrough

Walkthrough

Adds a new migration that adds a non-null created_at column to the templates table, updates the MIGRATIONS registry to include that migration, and extends the Template struct and related ops to carry a created_at: String with a default RFC3339 UTC value.

Changes

Cohort / File(s) Summary
Migration registration
crates/db-user/src/lib.rs
MIGRATIONS length changed from 27 to 28; appended "./templates_migration_2.sql" to the migrations array.
New database migration
crates/db-user/src/templates_migration_2.sql
SQL migration adding created_at column to templates as TEXT NOT NULL with default strftime('%Y-%m-%dT%H:%M:%SZ','now').
Template entity and ops
crates/db-user/src/templates_types.rs,
crates/db-user/src/templates_ops.rs
Added pub created_at: String to Template; added Template::default_created_at() returning RFC3339-like UTC string; from_row reads column 7 with fallback to default; tests/ops now provide created_at when upserting.

Sequence Diagram(s)

(omitted — changes are schema/struct additions with no new runtime control-flow to diagram)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify the SQL syntax and default expression are valid for the target SQLite/Postgres dialect used by the project.
  • Confirm from_row uses the correct column index (column 7) for created_at relative to SELECT ordering.
  • Check migration ordering and that adding a NOT NULL column with a default is safe for existing rows.
  • Review tests in templates_ops.rs to ensure they construct Template with created_at correctly.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "fix: add missing created_at column to templates table" directly and accurately reflects the primary change in the changeset. The changeset includes adding a migration file that adds a created_at column, updating the Template struct with a created_at field, and registering the migration in the MIGRATIONS array. The title is concise, clear, follows conventional commit format, and contains no vague language or extraneous noise. A teammate scanning commit history would immediately understand that this PR addresses a schema deficiency by adding the missing created_at column to the templates table.
Description Check ✅ Passed The PR description is well-related to the changeset and provides substantive context about the problem being solved and the solution implemented. It explains the schema mismatch across backend SQL, Rust, and frontend TypeScript; details the five-step solution with specific file names and components changed; and describes the business impact of these changes. The description is specific and meaningful rather than vague, mentioning concrete details like the migration file name, field names, column numbers, and affected systems like TinyBase.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fca2a2 and 21912cb.

📒 Files selected for processing (4)
  • crates/db-user/src/lib.rs (2 hunks)
  • crates/db-user/src/templates_migration_2.sql (1 hunks)
  • crates/db-user/src/templates_ops.rs (3 hunks)
  • crates/db-user/src/templates_types.rs (2 hunks)
🔇 Additional comments (6)
crates/db-user/src/lib.rs (1)

145-145: LGTM!

The migration is correctly registered by incrementing the array size and appending the new migration file. This follows the append-only policy stated in the comment.

Also applies to: 173-173

crates/db-user/src/templates_types.rs (2)

12-12: LGTM!

The created_at field is appropriately added to track template creation time.


39-41: LGTM with RFC3339 fallback.

The column index 7 is correct for created_at, and the fallback to RFC3339-formatted current time provides good backward compatibility. However, ensure the migration default format matches this RFC3339 format (see comment on templates_migration_2.sql).

crates/db-user/src/templates_ops.rs (3)

34-35: LGTM!

The created_at field is correctly added to the INSERT statement columns, VALUES placeholders, and named parameters. This ensures new templates receive the creation timestamp.

Also applies to: 43-44, 60-60


45-51: LGTM: Creation timestamp correctly preserved on update.

The ON CONFLICT DO UPDATE clause intentionally omits created_at, which is correct behavior—creation timestamps should not be modified when updating existing templates.


109-110: LGTM!

The test correctly uses RFC3339 format for created_at, consistent with the format used in templates_types.rs.

@coderabbitai
Copy link

coderabbitai bot commented Oct 26, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link

coderabbitai bot commented Oct 26, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #1601

coderabbitai bot added a commit that referenced this pull request Oct 26, 2025
Docstrings generation was requested by @dmfilipenko.

* #1600 (comment)

The following files were modified:

* `crates/db-user/src/lib.rs`
* `crates/db-user/src/templates_ops.rs`
* `crates/db-user/src/templates_types.rs`
## Problem
Templates table schema was missing the `created_at` column that the
frontend TinyBase store expected. This caused templates to fail loading
from SQLite into TinyBase, making user-created templates invisible in
the UI dropdown.

### Schema Mismatch:
- Backend SQL: Missing `created_at` column
- Backend Rust: Missing `created_at` field in Template struct
- Frontend TypeScript: Expected `created_at` field in TinyBase schema

## Solution
1. Created migration `templates_migration_2.sql` with proper ISO 8601 format
   - Uses `strftime('%Y-%m-%dT%H:%M:%SZ', 'now')` for correct formatting
   - Auto-populates `created_at` via DEFAULT (database-managed)
2. Updated Template struct with `created_at: String` field
   - Added `#[serde(default)]` for backward compatibility
   - Added `default_created_at()` helper for consistent timestamp generation
3. Updated `from_row()` to read `created_at` from column 7 with fallback
4. Updated `upsert_template()` to let SQLite manage `created_at`
   - Removed manual `created_at` parameter (prevents timestamp forgery)
   - Database now controls creation timestamps (audit-safe)
5. Updated test to use `default_created_at()` helper
6. Registered new migration in MIGRATIONS array

## Impact
- New templates have `created_at` automatically set by database
- Existing templates get `created_at` via migration
- TinyBase can now successfully load templates from SQLite
- Templates appear correctly in UI dropdown
- Timestamps are immutable and database-controlled (secure)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@dmfilipenko dmfilipenko force-pushed the fix/templates-created-at-schema branch from 21912cb to 9d2044a Compare October 26, 2025 13:21
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/db-user/src/templates_ops.rs (1)

22-65: Critical: created_at field is not persisted in the database.

The upsert_template function accepts a Template struct that now includes a created_at field (as added in this PR), but the SQL statement does not include created_at in either the INSERT columns or the UPDATE clause. This means:

  1. The created_at value passed to this function is silently ignored
  2. Only the migration's default value will be used for new rows
  3. Existing templates will never have their created_at updated

If created_at should be set explicitly on INSERT (and optionally on UPDATE), add it to the SQL statement:

 "INSERT INTO templates (
     id,
     user_id,
     title,
     description,
     sections,
     tags,
-    context_option
+    context_option,
+    created_at
 ) VALUES (
     :id,
     :user_id,
     :title,
     :description,
     :sections,
     :tags,
-    :context_option
+    :context_option,
+    :created_at
 ) ON CONFLICT(id) DO UPDATE SET
     title = :title,
     description = :description,
     sections = :sections,
     tags = :tags,
-    context_option = :context_option
+    context_option = :context_option,
+    created_at = :created_at
 RETURNING *",
 libsql::named_params! {
     ":id": template.id,
     ":user_id": template.user_id,
     ":title": template.title,
     ":description": template.description,
     ":sections": serde_json::to_string(&template.sections).unwrap(),
     ":tags": serde_json::to_string(&template.tags).unwrap(),
     ":context_option": template.context_option.as_deref().unwrap_or(""),
+    ":created_at": template.created_at,
 },

Note: If created_at should be immutable after creation, remove it from the ON CONFLICT DO UPDATE SET clause and only include it in the INSERT.

🧹 Nitpick comments (1)
crates/db-user/src/templates_ops.rs (1)

80-113: Test doesn't verify created_at persistence.

While the test correctly provides a created_at value when constructing the Template, it doesn't verify that this value is actually persisted to and retrieved from the database. Due to the SQL bug identified above (missing created_at in the INSERT statement), this test would pass even though the explicit created_at value is being ignored.

Consider adding an assertion to verify the created_at value:

 let templates = db.list_templates(&human.id).await.unwrap();
 assert_eq!(templates.len(), 1);
+assert_eq!(templates[0].created_at, _template.created_at);

This would catch the SQL bug and ensure created_at is properly round-tripped through the database.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21912cb and 9d2044a.

📒 Files selected for processing (4)
  • crates/db-user/src/lib.rs (2 hunks)
  • crates/db-user/src/templates_migration_2.sql (1 hunks)
  • crates/db-user/src/templates_ops.rs (1 hunks)
  • crates/db-user/src/templates_types.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • crates/db-user/src/templates_types.rs
  • crates/db-user/src/templates_migration_2.sql
🧰 Additional context used
🧬 Code graph analysis (1)
crates/db-user/src/templates_ops.rs (2)
plugins/db/js/bindings.gen.ts (1)
  • Template (202-202)
crates/db-user/src/templates_types.rs (1)
  • default_created_at (25-27)
🔇 Additional comments (1)
crates/db-user/src/lib.rs (1)

145-145: Migration registration and implementation verified.

The migration file exists and correctly adds the created_at column with a TEXT type, NOT NULL constraint, and ISO 8601 timestamp default value. The MIGRATIONS array size of 28 is accurate with the new entry appended to the previous 27.


// Append only. Do not reorder.
const MIGRATIONS: [&str; 27] = [
const MIGRATIONS: [&str; 28] = [
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify TypeScript bindings include the new created_at field.

The relevant code snippets show that the frontend TypeScript type definition for Template (in plugins/db/js/bindings.gen.ts line 201) does not include the created_at field that was added to the backend Rust struct. This inconsistency will cause type errors or runtime issues when the frontend tries to use the created_at field.

Check if the TypeScript bindings need to be regenerated:


🏁 Script executed:

#!/bin/bash
# Description: Check TypeScript Template type definition and look for binding generation commands

# Check current Template type definition
echo "=== Current TypeScript Template type ==="
rg -A2 "export type Template = " plugins/db/js/bindings.gen.ts

# Look for binding generation scripts or commands
echo ""
echo "=== Looking for binding generation commands ==="
rg -n "bindings.gen|specta|ts-rs" --type-add 'toml:*.toml' --type toml
rg -n "bindings.gen|specta|ts-rs" package.json 2>/dev/null || echo "No package.json commands found"

# Check if there's a build script or makefile
fd -t f "Makefile|justfile|build.rs" .

Length of output: 10746


Let me verify the backend struct definition and the build process:

Now let me check the plugins/db/build.rs to see how bindings are generated:

Now I need to check the actual Template struct definition and the build.rs file:


---

🏁 Script executed:

```shell
#!/bin/bash
# Look for Template struct definition with specta annotations

echo "=== Searching for Template struct definition ==="
rg -n "struct Template" -A5 --type rs

echo ""
echo "=== Check if Template has specta::Type derive ==="
rg -n "#\[derive.*Type" -B2 --type rs | grep -A2 "Template"

echo ""
echo "=== Check plugins/db/build.rs for binding generation ==="
cat ./plugins/db/build.rs | head -50

Length of output: 1379



---

🏁 Script executed:

```shell
#!/bin/bash
# Corrected approach to find Template struct

echo "=== Find files containing 'struct Template' ==="
fd -e rs -x rg -l "struct Template" {} \;

echo ""
echo "=== Search for Template struct definition ==="
rg -n "struct Template" -A10

echo ""
echo "=== Check build.rs for specta binding generation ==="
cat ./plugins/db/build.rs | grep -A20 "specta\|binding\|export"

echo ""
echo "=== Look for Template in plugins/db/src ==="
find ./plugins/db/src -name "*.rs" -exec grep -l "Template" {} \;

Length of output: 1773



---

🏁 Script executed:

```shell
#!/bin/bash
# Get full struct definition with all derives and attributes

echo "=== Full templates_types.rs file ==="
cat ./crates/db-user/src/templates_types.rs

echo ""
echo "=== Check if templates_types is exported from lib.rs ==="
rg -n "templates_types|pub.*Template" ./crates/db-user/src/lib.rs | head -20

echo ""
echo "=== Check how bindings are generated in plugins/db/build.rs ==="
tail -100 ./plugins/db/build.rs | head -50

Length of output: 2809



---

🏁 Script executed:

```shell
#!/bin/bash
# Find user_common_derives macro definition

echo "=== Search for user_common_derives! macro definition ==="
rg -n "macro_rules.*user_common_derives" -A20

echo ""
echo "=== Search for user_common_derives in use statements ==="
rg -n "user_common_derives" ./crates/db-user/src/lib.rs

echo ""
echo "=== Full plugins/db/build.rs to understand binding export ==="
cat ./plugins/db/build.rs

Length of output: 3069


Regenerate TypeScript bindings to include the new created_at field.

The backend Template struct in crates/db-user/src/templates_types.rs now includes the created_at: String field and is properly annotated with specta::Type via the user_common_derives! macro. However, the TypeScript bindings in plugins/db/js/bindings.gen.ts do not include this field, indicating the bindings are out of sync. Rebuild the project (e.g., cargo build) to regenerate the TypeScript bindings from the updated Rust types.

🤖 Prompt for AI Agents
In crates/db-user/src/lib.rs around line 145, the TypeScript bindings are out of
date and missing the new Template.created_at field; rebuild the Rust crate so
the specta-derived types regenerate the TS output. Run a full cargo build (or
cargo build -p db-user) to run the bindings generation step, then verify
plugins/db/js/bindings.gen.ts includes created_at: string and commit the updated
bindings file.

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.

1 participant