diff --git a/.github/workflows/llm-benchmark-update.yml b/.github/workflows/llm-benchmark-update.yml
index 3f72350e840..6e70689a385 100644
--- a/.github/workflows/llm-benchmark-update.yml
+++ b/.github/workflows/llm-benchmark-update.yml
@@ -28,7 +28,11 @@ jobs:
if: |
(github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/update-llm-benchmark')) ||
(github.event_name == 'workflow_dispatch')
- runs-on: ubuntu-latest
+ runs-on: spacetimedb-new-runner
+ container:
+ image: localhost:5000/spacetimedb-ci:latest
+ options: >-
+ --privileged
steps:
# Here we install the spacetime CLI for faster execution of the tests
# SpacetimeDB itself is not under test here, rather it's the docs.
@@ -201,6 +205,18 @@ jobs:
llm_benchmark ci-quickfix
llm_benchmark ci-check
+ # Generate failure analysis if there are any failures
+ - name: Generate failure analysis
+ env:
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
+ run: |
+ llm_benchmark analyze -o docs/llms/docs-benchmark-analysis.md || true
+
+ # Generate PR comment markdown (compares against master baseline)
+ - name: Generate PR comment markdown
+ run: |
+ llm_benchmark ci-comment
+
- name: Ensure only docs/llms changed
run: |
set -euo pipefail
@@ -226,77 +242,41 @@ jobs:
github-token: ${{ secrets.CLOCKWORK_LABS_BOT_PAT }}
script: |
const fs = require('fs');
- // docs-benchmark files are used for CI (testing documentation quality)
- const summaryPath = 'docs/llms/docs-benchmark-summary.json';
- const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
-
- // Extract results for the modes checked by ci-check
- // Rust: rustdoc_json, C#: docs
- const rustResults = summary.by_language?.rust?.modes?.rustdoc_json?.models?.['GPT-5'];
- const csharpResults = summary.by_language?.csharp?.modes?.docs?.models?.['GPT-5'];
-
- const formatPct = (val) => val !== undefined ? `${val.toFixed(1)}%` : 'N/A';
-
- let table = `## LLM Benchmark Results (ci-quickfix)\n\n`;
- table += `| Language | Mode | Category | Tests Passed | Pass % | Task Pass % |\n`;
- table += `|----------|------|----------|--------------|--------|-------------|\n`;
-
- if (rustResults) {
- const cats = rustResults.categories || {};
- if (cats.basics) {
- const c = cats.basics;
- table += `| Rust | rustdoc_json | basics | ${c.passed_tests}/${c.total_tests} | ${formatPct(c.pass_pct)} | ${formatPct(c.task_pass_pct)} |\n`;
- }
- if (cats.schema) {
- const c = cats.schema;
- table += `| Rust | rustdoc_json | schema | ${c.passed_tests}/${c.total_tests} | ${formatPct(c.pass_pct)} | ${formatPct(c.task_pass_pct)} |\n`;
- }
- const t = rustResults.totals;
- table += `| Rust | rustdoc_json | **total** | ${t.passed_tests}/${t.total_tests} | ${formatPct(t.pass_pct)} | ${formatPct(t.task_pass_pct)} |\n`;
- }
- if (csharpResults) {
- const cats = csharpResults.categories || {};
- if (cats.basics) {
- const c = cats.basics;
- table += `| C# | docs | basics | ${c.passed_tests}/${c.total_tests} | ${formatPct(c.pass_pct)} | ${formatPct(c.task_pass_pct)} |\n`;
- }
- if (cats.schema) {
- const c = cats.schema;
- table += `| C# | docs | schema | ${c.passed_tests}/${c.total_tests} | ${formatPct(c.pass_pct)} | ${formatPct(c.task_pass_pct)} |\n`;
+ // Read the pre-generated comment markdown
+ const commentPath = 'docs/llms/docs-benchmark-comment.md';
+ if (!fs.existsSync(commentPath)) {
+ core.setFailed(`Comment file not found: ${commentPath}`);
+ return;
+ }
+ let body = fs.readFileSync(commentPath, 'utf8');
+
+ // Check if failure analysis exists and append it
+ const analysisPath = 'docs/llms/docs-benchmark-analysis.md';
+ if (fs.existsSync(analysisPath)) {
+ const analysis = fs.readFileSync(analysisPath, 'utf8');
+ // Only include if there's meaningful content (not just "no failures")
+ if (!analysis.includes('No failures found')) {
+ body += `\n\nFailure Analysis (click to expand)
\n\n${analysis}\n `;
}
- const t = csharpResults.totals;
- table += `| C# | docs | **total** | ${t.passed_tests}/${t.total_tests} | ${formatPct(t.pass_pct)} | ${formatPct(t.task_pass_pct)} |\n`;
}
- table += `\nGenerated at: ${summary.generated_at}`;
-
const issue_number = Number(process.env.PR_NUMBER);
- // Find and update existing comment or create new one
- const comments = await github.rest.issues.listComments({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number,
- });
-
- const marker = '## LLM Benchmark Results (ci-quickfix)';
- const existingComment = comments.data.find(c => c.body.startsWith(marker));
-
- if (existingComment) {
- await github.rest.issues.updateComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- comment_id: existingComment.id,
- body: table,
- });
- } else {
+ // Always post a new comment
+ console.log(`Posting new comment on PR #${issue_number}...`);
+ try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number,
- body: table,
+ body,
});
+ console.log('Comment created successfully');
+ } catch (err) {
+ console.error('Failed to post comment:', err.message);
+ console.error('Full error:', JSON.stringify(err, null, 2));
+ throw err;
}
# The benchmarks only modify the docs/llms directory.
diff --git a/Cargo.lock b/Cargo.lock
index 168fffc3df7..8cccfd27ce0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11008,6 +11008,7 @@ dependencies = [
"fs2",
"futures",
"heck 0.5.0",
+ "regex",
"reqwest 0.12.24",
"serde",
"serde_json",
diff --git a/crates/cli/build.rs b/crates/cli/build.rs
index e49f1e0cbad..2eefa166acb 100644
--- a/crates/cli/build.rs
+++ b/crates/cli/build.rs
@@ -58,6 +58,8 @@ fn get_manifest_dir() -> PathBuf {
// templates list at templates/templates-list.json
// * `get_ai_rules_base` - returns base AI rules for all languages
// * `get_ai_rules_typescript` - returns TypeScript-specific AI rules
+// * `get_ai_rules_rust` - returns Rust-specific AI rules
+// * `get_ai_rules_csharp` - returns C#-specific AI rules
fn generate_template_files() {
let manifest_dir = get_manifest_dir();
let repo_root = get_repo_root();
@@ -140,6 +142,34 @@ fn generate_template_files() {
panic!("Could not find \"docs/static/ai-rules/spacetimedb-typescript.mdc\" file.");
}
+ // Rust-specific rules
+ let rust_rules_path = ai_rules_dir.join("spacetimedb-rust.mdc");
+ if rust_rules_path.exists() {
+ generated_code.push_str("pub fn get_ai_rules_rust() -> &'static str {\n");
+ generated_code.push_str(&format!(
+ " include_str!(\"{}\")\n",
+ rust_rules_path.to_str().unwrap().replace('\\', "\\\\")
+ ));
+ generated_code.push_str("}\n\n");
+ println!("cargo:rerun-if-changed={}", rust_rules_path.display());
+ } else {
+ panic!("Could not find \"docs/static/ai-rules/spacetimedb-rust.mdc\" file.");
+ }
+
+ // C#-specific rules
+ let csharp_rules_path = ai_rules_dir.join("spacetimedb-csharp.mdc");
+ if csharp_rules_path.exists() {
+ generated_code.push_str("pub fn get_ai_rules_csharp() -> &'static str {\n");
+ generated_code.push_str(&format!(
+ " include_str!(\"{}\")\n",
+ csharp_rules_path.to_str().unwrap().replace('\\', "\\\\")
+ ));
+ generated_code.push_str("}\n\n");
+ println!("cargo:rerun-if-changed={}", csharp_rules_path.display());
+ } else {
+ panic!("Could not find \"docs/static/ai-rules/spacetimedb-csharp.mdc\" file.");
+ }
+
// Expose workspace metadata so `spacetime init` can rewrite template manifests without hardcoding versions.
generated_code.push_str("pub fn get_workspace_edition() -> &'static str {\n");
generated_code.push_str(&format!(" \"{}\"\n", workspace_edition.escape_default()));
diff --git a/crates/cli/src/subcommands/init.rs b/crates/cli/src/subcommands/init.rs
index 8530bde2933..bae22796d3c 100644
--- a/crates/cli/src/subcommands/init.rs
+++ b/crates/cli/src/subcommands/init.rs
@@ -1667,15 +1667,22 @@ fn set_dependency_version(item: &mut Item, version: &str, remove_path: bool) {
/// Writes rules to:
/// - .cursor/rules/ (Cursor)
/// - CLAUDE.md (Claude Code)
+/// - AGENTS.md (Opencode)
/// - .windsurfrules (Windsurf)
/// - .github/copilot-instructions.md (VS Code Copilot)
fn install_ai_rules(config: &TemplateConfig, project_path: &Path) -> anyhow::Result<()> {
let base_rules = embedded::get_ai_rules_base();
let ts_rules = embedded::get_ai_rules_typescript();
+ let rust_rules = embedded::get_ai_rules_rust();
+ let csharp_rules = embedded::get_ai_rules_csharp();
- // Check if TypeScript is used in either server or client
+ // Check which languages are used in server or client
let uses_typescript = config.server_lang == Some(ServerLanguage::TypeScript)
|| config.client_lang == Some(ClientLanguage::TypeScript);
+ let uses_rust =
+ config.server_lang == Some(ServerLanguage::Rust) || config.client_lang == Some(ClientLanguage::Rust);
+ let uses_csharp =
+ config.server_lang == Some(ServerLanguage::Csharp) || config.client_lang == Some(ClientLanguage::Csharp);
// 1. Cursor: .cursor/rules/ directory with separate files
let cursor_dir = project_path.join(".cursor/rules");
@@ -1684,24 +1691,44 @@ fn install_ai_rules(config: &TemplateConfig, project_path: &Path) -> anyhow::Res
if uses_typescript {
fs::write(cursor_dir.join("spacetimedb-typescript.mdc"), ts_rules)?;
}
+ if uses_rust {
+ fs::write(cursor_dir.join("spacetimedb-rust.mdc"), rust_rules)?;
+ }
+ if uses_csharp {
+ fs::write(cursor_dir.join("spacetimedb-csharp.mdc"), csharp_rules)?;
+ }
// Build combined content for single-file AI assistants
// Strip the YAML frontmatter from the .mdc files for non-Cursor tools
let base_content = strip_mdc_frontmatter(base_rules);
- let combined_content = if uses_typescript {
+ let mut combined_content = base_content.to_string();
+
+ if uses_typescript {
let ts_content = strip_mdc_frontmatter(ts_rules);
- format!("{}\n\n{}", base_content, ts_content)
- } else {
- base_content.to_string()
- };
+ combined_content.push_str("\n\n");
+ combined_content.push_str(ts_content);
+ }
+ if uses_rust {
+ let rust_content = strip_mdc_frontmatter(rust_rules);
+ combined_content.push_str("\n\n");
+ combined_content.push_str(rust_content);
+ }
+ if uses_csharp {
+ let csharp_content = strip_mdc_frontmatter(csharp_rules);
+ combined_content.push_str("\n\n");
+ combined_content.push_str(csharp_content);
+ }
// 2. Claude Code: CLAUDE.md
fs::write(project_path.join("CLAUDE.md"), &combined_content)?;
- // 3. Windsurf: .windsurfrules
+ // 3. Opencode: AGENTS.md
+ fs::write(project_path.join("AGENTS.md"), &combined_content)?;
+
+ // 4. Windsurf: .windsurfrules
fs::write(project_path.join(".windsurfrules"), &combined_content)?;
- // 4. VS Code Copilot: .github/copilot-instructions.md
+ // 5. VS Code Copilot: .github/copilot-instructions.md
let github_dir = project_path.join(".github");
fs::create_dir_all(&github_dir)?;
fs::write(github_dir.join("copilot-instructions.md"), &combined_content)?;
diff --git a/docs/DEVELOP.md b/docs/DEVELOP.md
index f4e614823fc..79a73d8c6d6 100644
--- a/docs/DEVELOP.md
+++ b/docs/DEVELOP.md
@@ -9,7 +9,8 @@ This document explains how to configure the environment, run the LLM benchmark t
1. [Quick Checks & Fixes](#quick-checks-fixes)
2. [Environment Variables](#environment-variables)
3. [Benchmark Suite](#benchmark-suite)
-4. [Troubleshooting](#troubleshooting)
+4. [Context Construction](#context-construction)
+5. [Troubleshooting](#troubleshooting)
---
## Quick Checks & Fixes
@@ -231,6 +232,11 @@ cargo llm run --force
cargo llm ci-check --lang rust
cargo llm ci-check --lang csharp
+# Generate PR comment markdown (compares against master baseline)
+cargo llm ci-comment
+# With custom baseline ref
+cargo llm ci-comment --baseline-ref origin/main
+
```
Outputs:
@@ -239,6 +245,62 @@ Outputs:
---
+## Context Construction
+
+The benchmark tool constructs a context (documentation) that is sent to the LLM along with each task prompt. The context varies by language and mode.
+
+### Modes
+
+| Mode | Language | Source | Description |
+|------|----------|--------|-------------|
+| `rustdoc_json` | Rust | `crates/bindings` | Generates rustdoc JSON and extracts documentation from the spacetimedb crate |
+| `docs` | C# | `docs/docs/**/*.md` | Concatenates all markdown files from the documentation |
+
+### Tab Filtering
+
+When building context for a specific language, the tool filters `` components to only include content relevant to the target language. This reduces noise and helps the LLM focus on the correct syntax.
+
+**Filtered tab groupIds:**
+
+| groupId | Purpose | Tab Values |
+|---------|---------|------------|
+| `server-language` | Server module code examples | `rust`, `csharp`, `typescript` |
+| `client-language` | Client SDK code examples | `rust`, `csharp`, `typescript`, `cpp`, `blueprint` |
+
+**Filtering behavior:**
+- For C# tests: Only `value="csharp"` tabs are kept
+- For Rust tests: Only `value="rust"` tabs are kept
+- If no matching tab exists (e.g., `client-language` with only `cpp`/`blueprint`), the entire tabs block is removed
+
+**Example transformation:**
+
+Before (in markdown):
+```html
+
+
+C# code here
+
+
+Rust code here
+
+
+```
+
+After (for C# context):
+```
+C# code here
+```
+
+### Documentation Best Practices
+
+When writing documentation that will be used by the benchmark:
+
+1. **Use consistent tab groupIds**: Always use `server-language` for server module code and `client-language` for client SDK code
+2. **Include all supported languages**: Ensure each `` block has tabs for all languages you want to test
+3. **Use consistent naming conventions**: The benchmark compares LLM output against golden answers, so documentation should reflect the expected conventions (e.g., PascalCase table names for C#)
+
+---
+
## Troubleshooting
**HTTP 400/404 from providers**
diff --git a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md
index ab5fd13cc52..c1fe10ee679 100644
--- a/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md
+++ b/docs/docs/00100-intro/00100-getting-started/00400-key-architecture.md
@@ -47,7 +47,7 @@ const players = table(
```csharp
-[SpacetimeDB.Table(Name = "players", Public = true)]
+[SpacetimeDB.Table(Name = "Player", Public = true)]
public partial struct Player
{
[SpacetimeDB.PrimaryKey]
@@ -193,7 +193,7 @@ spacetimedb.reducer('world', (ctx) => {
```
While SpacetimeDB doesn't support nested transactions,
-a reducer can [schedule another reducer](/tables/scheduled-tables) to run at an interval,
+a reducer can [schedule another reducer](/tables/schedule-tables) to run at an interval,
or at a specific time.
@@ -218,7 +218,7 @@ public static void World(ReducerContext ctx)
```
While SpacetimeDB doesn't support nested transactions,
-a reducer can [schedule another reducer](/tables/scheduled-tables) to run at an interval,
+a reducer can [schedule another reducer](/tables/schedule-tables) to run at an interval,
or at a specific time.
diff --git a/docs/docs/00100-intro/00200-quickstarts/00100-react.md b/docs/docs/00100-intro/00200-quickstarts/00100-react.md
index c254e9ca2de..836849a8f66 100644
--- a/docs/docs/00100-intro/00200-quickstarts/00100-react.md
+++ b/docs/docs/00100-intro/00200-quickstarts/00100-react.md
@@ -29,7 +29,7 @@ Get a SpacetimeDB React app running in under 5 minutes.
```bash
-spacetime dev --template basic-react my-spacetime-app
+spacetime dev --template basic-react
```
@@ -59,6 +59,65 @@ my-spacetime-app/
│ ├── App.tsx
│ └── module_bindings/ # Auto-generated types
└── package.json
+```
+
+
+
+
+
+ Open `spacetimedb/src/index.ts` to see the module code. The template includes a `person` table and two reducers: `add` to insert a person, and `say_hello` to greet everyone.
+
+ Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
+
+
+```typescript
+import { schema, table, t } from 'spacetimedb/server';
+
+export const spacetimedb = schema(
+ table(
+ { name: 'person', public: true },
+ {
+ name: t.string(),
+ }
+ )
+);
+
+spacetimedb.reducer('add', { name: t.string() }, (ctx, { name }) => {
+ ctx.db.person.insert({ name });
+});
+
+spacetimedb.reducer('say_hello', (ctx) => {
+ for (const person of ctx.db.person.iter()) {
+ console.info(`Hello, ${person.name}!`);
+ }
+ console.info('Hello, World!');
+});
+```
+
+
+
+
+
+ Use the SpacetimeDB CLI to call reducers and query your data directly.
+
+
+```bash
+# Call the add reducer to insert a person
+spacetime call add Alice
+
+# Query the person table
+spacetime sql "SELECT * FROM person"
+ name
+---------
+ "Alice"
+
+# Call say_hello to greet everyone
+spacetime call say_hello
+
+# View the module logs
+spacetime logs
+2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
+2025-01-13T12:00:00.000000Z INFO: Hello, World!
```
diff --git a/docs/docs/00100-intro/00200-quickstarts/00400-typescript.md b/docs/docs/00100-intro/00200-quickstarts/00400-typescript.md
index 4510c2e57ca..f0f008d3885 100644
--- a/docs/docs/00100-intro/00200-quickstarts/00400-typescript.md
+++ b/docs/docs/00100-intro/00200-quickstarts/00400-typescript.md
@@ -29,7 +29,7 @@ Get a SpacetimeDB TypeScript app running in under 5 minutes.
```bash
-spacetime dev --template basic-typescript my-spacetime-app
+spacetime dev --template basic-typescript
```
@@ -55,17 +55,61 @@ my-spacetime-app/
-
+
- Use the CLI to interact with your running module. Call reducers and query data directly.
+ Open `spacetimedb/src/index.ts` to see the module code. The template includes a `person` table and two reducers: `add` to insert a person, and `say_hello` to greet everyone.
+
+ Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
+
+
+```typescript
+import { schema, table, t } from 'spacetimedb/server';
+
+export const spacetimedb = schema(
+ table(
+ { name: 'person' },
+ {
+ name: t.string(),
+ }
+ )
+);
+
+spacetimedb.reducer('add', { name: t.string() }, (ctx, { name }) => {
+ ctx.db.person.insert({ name });
+});
+
+spacetimedb.reducer('say_hello', (ctx) => {
+ for (const person of ctx.db.person.iter()) {
+ console.info(`Hello, ${person.name}!`);
+ }
+ console.info('Hello, World!');
+});
+```
+
+
+
+
+
+ Use the SpacetimeDB CLI to call reducers and query your data directly.
```bash
-# Call a reducer
-spacetime call --server local my-spacetime-app your_reducer "arg1"
+# Call the add reducer to insert a person
+spacetime call add Alice
+
+# Query the person table
+spacetime sql "SELECT * FROM person"
+ name
+---------
+ "Alice"
+
+# Call say_hello to greet everyone
+spacetime call say_hello
-# Query your data
-spacetime sql --server local my-spacetime-app "SELECT * FROM your_table"
+# View the module logs
+spacetime logs
+2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
+2025-01-13T12:00:00.000000Z INFO: Hello, World!
```
diff --git a/docs/docs/00100-intro/00200-quickstarts/00500-rust.md b/docs/docs/00100-intro/00200-quickstarts/00500-rust.md
index ec33acbc918..2ed4351266a 100644
--- a/docs/docs/00100-intro/00200-quickstarts/00500-rust.md
+++ b/docs/docs/00100-intro/00200-quickstarts/00500-rust.md
@@ -57,17 +57,59 @@ my-spacetime-app/
-
+
- Use the CLI to interact with your running module. Call reducers and query data directly.
+ Open `spacetimedb/src/lib.rs` to see the module code. The template includes a `Person` table and two reducers: `add` to insert a person, and `say_hello` to greet everyone.
+
+ Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
+
+
+```rust
+use spacetimedb::{ReducerContext, Table};
+
+#[spacetimedb::table(name = person, public)]
+pub struct Person {
+ name: String,
+}
+
+#[spacetimedb::reducer]
+pub fn add(ctx: &ReducerContext, name: String) {
+ ctx.db.person().insert(Person { name });
+}
+
+#[spacetimedb::reducer]
+pub fn say_hello(ctx: &ReducerContext) {
+ for person in ctx.db.person().iter() {
+ log::info!("Hello, {}!", person.name);
+ }
+ log::info!("Hello, World!");
+}
+```
+
+
+
+
+
+ Use the SpacetimeDB CLI to call reducers and query your data directly.
```bash
-# Call a reducer
-spacetime call --server local my-spacetime-app your_reducer "arg1"
+# Call the add reducer to insert a person
+spacetime call my-spacetime-app add Alice
+
+# Query the person table
+spacetime sql my-spacetime-app "SELECT * FROM person"
+ name
+---------
+ "Alice"
+
+# Call say_hello to greet everyone
+spacetime call my-spacetime-app say_hello
-# Query your data
-spacetime sql --server local my-spacetime-app "SELECT * FROM your_table"
+# View the module logs
+spacetime logs my-spacetime-app
+2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
+2025-01-13T12:00:00.000000Z INFO: Hello, World!
```
diff --git a/docs/docs/00100-intro/00200-quickstarts/00600-c-sharp.md b/docs/docs/00100-intro/00200-quickstarts/00600-c-sharp.md
index eb6ddb5e2fa..a70d44ccf1e 100644
--- a/docs/docs/00100-intro/00200-quickstarts/00600-c-sharp.md
+++ b/docs/docs/00100-intro/00200-quickstarts/00600-c-sharp.md
@@ -40,7 +40,7 @@ dotnet workload install wasi-experimental
```bash
-spacetime dev --template basic-c-sharp my-spacetime-app
+spacetime dev --template basic-c-sharp
```
@@ -66,17 +66,66 @@ my-spacetime-app/
-
+
- Use the CLI to interact with your running module. Call reducers and query data directly.
+ Open `spacetimedb/Lib.cs` to see the module code. The template includes a `Person` table and two reducers: `Add` to insert a person, and `SayHello` to greet everyone.
+
+ Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
-```bash
-# Call a reducer
-spacetime call --server local my-spacetime-app YourReducer "arg1"
+```csharp
+using SpacetimeDB;
+
+public static partial class Module
+{
+ [SpacetimeDB.Table(Name = "Person", Public = true)]
+ public partial struct Person
+ {
+ public string Name;
+ }
+
+ [SpacetimeDB.Reducer]
+ public static void Add(ReducerContext ctx, string name)
+ {
+ ctx.Db.Person.Insert(new Person { Name = name });
+ }
+
+ [SpacetimeDB.Reducer]
+ public static void SayHello(ReducerContext ctx)
+ {
+ foreach (var person in ctx.Db.Person.Iter())
+ {
+ Log.Info($"Hello, {person.Name}!");
+ }
+ Log.Info("Hello, World!");
+ }
+}
+```
+
+
-# Query your data
-spacetime sql --server local my-spacetime-app "SELECT * FROM your_table"
+
+
+ Use the SpacetimeDB CLI to call reducers and query your data directly.
+
+
+```bash
+# Call the add reducer to insert a person
+spacetime call Add Alice
+
+# Query the person table
+spacetime sql "SELECT * FROM Person"
+ name
+---------
+ "Alice"
+
+# Call say_hello to greet everyone
+spacetime call SayHello
+
+# View the module logs
+spacetime logs
+2025-01-13T12:00:00.000000Z INFO: Hello, Alice!
+2025-01-13T12:00:00.000000Z INFO: Hello, World!
```
diff --git a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md
index 14064ef614d..cddfb06f876 100644
--- a/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md
+++ b/docs/docs/00100-intro/00300-tutorials/00100-chat-app.md
@@ -242,7 +242,7 @@ const spacetimedb = schema(User, Message);
In `spacetimedb/Lib.cs`, add the definition of the tables to the `Module` class:
```csharp server
-[Table(Name = "user", Public = true)]
+[Table(Name = "User", Public = true)]
public partial class User
{
[PrimaryKey]
@@ -251,7 +251,7 @@ public partial class User
public bool Online;
}
-[Table(Name = "message", Public = true)]
+[Table(Name = "Message", Public = true)]
public partial class Message
{
public Identity Sender;
@@ -322,10 +322,10 @@ public static void SetName(ReducerContext ctx, string name)
{
name = ValidateName(name);
- if (ctx.Db.user.Identity.Find(ctx.Sender) is User user)
+ if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
{
user.Name = name;
- ctx.Db.user.Identity.Update(user);
+ ctx.Db.User.Identity.Update(user);
}
}
@@ -408,7 +408,7 @@ public static void SendMessage(ReducerContext ctx, string text)
{
text = ValidateMessage(text);
Log.Info(text);
- ctx.Db.message.Insert(
+ ctx.Db.Message.Insert(
new Message
{
Sender = ctx.Sender,
@@ -506,14 +506,14 @@ public static void ClientConnected(ReducerContext ctx)
{
Log.Info($"Connect {ctx.Sender}");
- if (ctx.Db.user.Identity.Find(ctx.Sender) is User user)
+ if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
{
user.Online = true;
- ctx.Db.user.Identity.Update(user);
+ ctx.Db.User.Identity.Update(user);
}
else
{
- ctx.Db.user.Insert(
+ ctx.Db.User.Insert(
new User
{
Name = null,
@@ -527,10 +527,10 @@ public static void ClientConnected(ReducerContext ctx)
[Reducer(ReducerKind.ClientDisconnected)]
public static void ClientDisconnected(ReducerContext ctx)
{
- if (ctx.Db.user.Identity.Find(ctx.Sender) is User user)
+ if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
{
user.Online = false;
- ctx.Db.user.Identity.Update(user);
+ ctx.Db.User.Identity.Update(user);
}
else
{
diff --git a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md
index 5946885f80e..a00681a6c49 100644
--- a/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md
+++ b/docs/docs/00100-intro/00300-tutorials/00300-unity-tutorial/00400-part-3.md
@@ -177,7 +177,7 @@ public partial struct SpawnFoodTimer
}
```
-Note the `Scheduled = nameof(SpawnFood)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `SpawnFood` reducer should be called. Each scheduled table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
+Note the `Scheduled = nameof(SpawnFood)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `SpawnFood` reducer should be called. Each schedule table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
@@ -193,7 +193,7 @@ pub struct SpawnFoodTimer {
}
```
-Note the `scheduled(spawn_food)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `spawn_food` reducer should be called. Each scheduled table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
+Note the `scheduled(spawn_food)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `spawn_food` reducer should be called. Each schedule table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
diff --git a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md
index e2b9fe321f8..859ab5157e9 100644
--- a/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md
+++ b/docs/docs/00100-intro/00300-tutorials/00400-unreal-tutorial/00400-part-3.md
@@ -177,7 +177,7 @@ public partial struct SpawnFoodTimer
}
```
-Note the `Scheduled = nameof(SpawnFood)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `SpawnFood` reducer should be called. Each scheduled table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
+Note the `Scheduled = nameof(SpawnFood)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `SpawnFood` reducer should be called. Each schedule table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
In order to schedule a reducer to be called we have to create a new table which specifies when and how a reducer should be called. Add this new table to the top of the file, below your imports.
@@ -192,7 +192,7 @@ pub struct SpawnFoodTimer {
}
```
-Note the `scheduled(spawn_food)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `spawn_food` reducer should be called. Each scheduled table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
+Note the `scheduled(spawn_food)` parameter in the table macro. This tells SpacetimeDB that the rows in this table specify a schedule for when the `spawn_food` reducer should be called. Each schedule table requires a `scheduled_id` and a `scheduled_at` field so that SpacetimeDB can call your reducer, however you can also add your own fields to these rows as well.
diff --git a/docs/docs/00200-core-concepts/00000-index.md b/docs/docs/00200-core-concepts/00000-index.md
index 9be4cac09a1..1934955e77e 100644
--- a/docs/docs/00200-core-concepts/00000-index.md
+++ b/docs/docs/00200-core-concepts/00000-index.md
@@ -19,10 +19,10 @@ Learn how SpacetimeDB databases work, including modules, publishing, and transac
Define your data model with tables, columns, and indexes.
- [Tables Overview](/tables) - Declaring and using tables
-- [Columns & Types](/tables/columns) - Supported column types
+- [Column Types](/tables/column-types) - Supported column types
- [Indexes](/tables/indexes) - Optimizing queries with indexes
- [Access Permissions](/tables/access-permissions) - Public vs private tables
-- [Scheduled Tables](/tables/scheduled-tables) - Time-based operations
+- [Schedule Tables](/tables/schedule-tables) - Time-based operations
## Functions
diff --git a/docs/docs/00200-core-concepts/00100-databases.md b/docs/docs/00200-core-concepts/00100-databases.md
index db62e78feab..107cadc80cc 100644
--- a/docs/docs/00200-core-concepts/00100-databases.md
+++ b/docs/docs/00200-core-concepts/00100-databases.md
@@ -216,7 +216,7 @@ Ready to level up? Dive into these advanced capabilities:
- **[Procedures](/functions/procedures)** - Make HTTP requests and interact with external services
- **[Views](/functions/views)** - Create computed, subscribable queries
-- **[Scheduled Tables](/tables/scheduled-tables)** - Schedule reducers to run at specific times
+- **[Schedule Tables](/tables/schedule-tables)** - Schedule reducers to run at specific times
- **[Incremental Migrations](/databases/incremental-migrations)** - Handle complex schema changes
- **[SQL Queries](/reference/sql)** - Query your database with SQL
diff --git a/docs/docs/00200-core-concepts/00100-databases/00100-transactions-atomicity.md b/docs/docs/00200-core-concepts/00100-databases/00100-transactions-atomicity.md
index 1fe22d165fe..7324e3cf298 100644
--- a/docs/docs/00200-core-concepts/00100-databases/00100-transactions-atomicity.md
+++ b/docs/docs/00200-core-concepts/00100-databases/00100-transactions-atomicity.md
@@ -156,7 +156,7 @@ pub fn child_reducer(ctx: &ReducerContext) -> Result<(), String> {
:::important
-SpacetimeDB does **not** support nested transactions. Nested reducer calls execute in the same transaction as their parent. If you need separate transactions, use [scheduled reducers](/tables/scheduled-tables) instead.
+SpacetimeDB does **not** support nested transactions. Nested reducer calls execute in the same transaction as their parent. If you need separate transactions, use [scheduled reducers](/tables/schedule-tables) instead.
:::
### Procedures: Manual Transactions
@@ -187,7 +187,7 @@ See [Procedures](/functions/procedures) for more details on manual transaction m
### No Nested Transactions
-SpacetimeDB does not support nested transactions. When one reducer calls another, they share the same transaction. If you need separate transactions, use [scheduled reducers](/tables/scheduled-tables) to trigger the second reducer asynchronously.
+SpacetimeDB does not support nested transactions. When one reducer calls another, they share the same transaction. If you need separate transactions, use [scheduled reducers](/tables/schedule-tables) to trigger the second reducer asynchronously.
### Auto-Increment is Not Transactional
@@ -200,5 +200,5 @@ The `#[auto_inc]` sequence generator is not transactional:
- **[Reducers](/functions/reducers)** - Functions that modify database state transactionally
- **[Procedures](/functions/procedures)** - Functions with manual transaction control
-- **[Scheduled Tables](/tables/scheduled-tables)** - Schedule reducers for separate transactions
+- **[Schedule Tables](/tables/schedule-tables)** - Schedule reducers for separate transactions
- **[Subscriptions](/subscriptions)** - How clients receive transactional updates
diff --git a/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md b/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md
index 2953995b383..5377a63cdd9 100644
--- a/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md
+++ b/docs/docs/00200-core-concepts/00100-databases/00500-cheat-sheet.md
@@ -293,7 +293,7 @@ pub fn on_disconnect(ctx: &ReducerContext) { /* ... */ }
-## Scheduled Tables
+## Schedule Tables
diff --git a/docs/docs/00200-core-concepts/00200-functions.md b/docs/docs/00200-core-concepts/00200-functions.md
index 3fd44c691e6..b7fa2ff66d9 100644
--- a/docs/docs/00200-core-concepts/00200-functions.md
+++ b/docs/docs/00200-core-concepts/00200-functions.md
@@ -3,17 +3,18 @@ title: Functions
slug: /functions
---
-
-Property / Characteristic | Reducers | Procedures | Views
--- | -- | -- | --
-Read from tables | ✓ | ✓ | ✓
-Write to tables | ✓ | ✓ |
-Runs in transaction by default | ✓ | |
-Atomic | ✓ | (manual) | ✓
-Deterministic | ✓ | | ✓
-External I/O (HTTP, etc.) | | ✓ |
-Side-effecting | | ✓ |
-Schedulable | ✓ | ✓ |
+import { Check } from '@site/src/components/Check';
+
+| Property / Characteristic | Reducers | Procedures | Views |
+|---------------------------|----------|------------|-------|
+| Read from tables | | | |
+| Write to tables | | | |
+| Runs in transaction by default | | | |
+| Atomic | | (manual) | |
+| Deterministic | | | |
+| External I/O (HTTP, etc.) | | | |
+| Side-effecting | | | |
+| Schedulable | | | |
SpacetimeDB modules can export three types of functions that clients can interact with:
@@ -33,4 +34,6 @@ Procedures are currently in beta and should only be used when you need their spe
**[Views](/functions/views)** are read-only functions that compute and return results from your tables. Unlike reducers and procedures, views do not modify database state - they only query and return data. Views are useful for computing derived data, aggregations, or joining multiple tables server-side before sending results to clients.
+Views run within a transaction to ensure isolation: the database state remains consistent for the entire duration of the view's execution. This means a view will never see partial updates from concurrent reducers.
+
Views can be subscribed to just like tables and will automatically update clients when underlying data changes, making them ideal for real-time computed data.
diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md
index e51959cd996..9a1425483ad 100644
--- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md
+++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00300-reducers.md
@@ -98,6 +98,16 @@ pub fn create_user(ctx: &ReducerContext, name: String, email: String) -> Result<
Reducers must take `&ReducerContext` as their first parameter. Additional parameters must be serializable types. Reducers can return `()`, `Result<(), String>`, or `Result<(), E>` where `E: Display`.
+:::note Rust: Importing the Table Trait
+Table operations like `insert`, `try_insert`, `iter`, and `count` are provided by the `Table` trait. You must import this trait for these methods to be available:
+
+```rust
+use spacetimedb::Table;
+```
+
+If you see errors like "no method named `try_insert` found", add this import.
+:::
+
@@ -113,14 +123,275 @@ If a reducer throws an exception or returns an error, all of its changes are aut
## Accessing Tables
-Reducers can query and modify tables through the `ReducerContext`:
+Reducers have full read-write access to all tables (both public and private) through the `ReducerContext`. The examples below assume a `user` table with `id` (primary key), `name` (indexed), and `email` (unique) columns.
+
+### Inserting Rows
+
+
+
+
+```typescript
+ctx.db.user.insert({
+ id: 0, // auto-increment will assign
+ name: 'Alice',
+ email: 'alice@example.com'
+});
+```
+
+
+
+
+```csharp
+ctx.Db.User.Insert(new User
+{
+ Id = 0, // auto-increment will assign
+ Name = "Alice",
+ Email = "alice@example.com"
+});
+```
+
+
+
+
+```rust
+ctx.db.user().insert(User {
+ id: 0, // auto-increment will assign
+ name: "Alice".to_string(),
+ email: "alice@example.com".to_string(),
+});
+```
+
+
+
+
+### Finding Rows by Unique Column
+
+Use `find` on a unique or primary key column to retrieve a single row:
+
+
+
+
+```typescript
+const user = ctx.db.user.id.find(123);
+if (user) {
+ console.log(`Found: ${user.name}`);
+}
+
+const byEmail = ctx.db.user.email.find('alice@example.com');
+```
+
+
+
+
+```csharp
+var user = ctx.Db.User.Id.Find(123);
+if (user is not null)
+{
+ Log.Info($"Found: {user.Name}");
+}
+
+var byEmail = ctx.Db.User.Email.Find("alice@example.com");
+```
+
+
+
+
+```rust
+if let Some(user) = ctx.db.user().id().find(123) {
+ log::info!("Found: {}", user.name);
+}
+
+let by_email = ctx.db.user().email().find("alice@example.com");
+```
+
+
+
+
+### Filtering Rows by Indexed Column
+
+Use `filter` on an indexed column to retrieve multiple matching rows:
+
+
+
+
+```typescript
+for (const user of ctx.db.user.name.filter('Alice')) {
+ console.log(`User ${user.id}: ${user.email}`);
+}
+```
+
+
+
+
+```csharp
+foreach (var user in ctx.Db.User.Name.Filter("Alice"))
+{
+ Log.Info($"User {user.Id}: {user.Email}");
+}
+```
+
+
+
+
+```rust
+for user in ctx.db.user().name().filter("Alice") {
+ log::info!("User {}: {}", user.id, user.email);
+}
+```
+
+
+
+
+### Updating Rows
+
+Find a row, modify it, then call `update` on the same unique column:
+
+
+
+
+```typescript
+const user = ctx.db.user.id.find(123);
+if (user) {
+ user.name = 'Bob';
+ ctx.db.user.id.update(user);
+}
+```
+
+
+
+
+```csharp
+var user = ctx.Db.User.Id.Find(123);
+if (user is not null)
+{
+ user.Name = "Bob";
+ ctx.Db.User.Id.Update(user);
+}
+```
+
+
+
+
+```rust
+if let Some(mut user) = ctx.db.user().id().find(123) {
+ user.name = "Bob".to_string();
+ ctx.db.user().id().update(user);
+}
+```
+
+
+
+
+### Deleting Rows
+
+Delete by unique column value or by indexed column value:
+
+
+
+
+```typescript
+// Delete by primary key
+ctx.db.user.id.delete(123);
+
+// Delete all matching an indexed column
+const deleted = ctx.db.user.name.delete('Alice');
+console.log(`Deleted ${deleted} row(s)`);
+```
+
+
+
+
+```csharp
+// Delete by primary key
+ctx.Db.User.Id.Delete(123);
+
+// Delete all matching an indexed column
+var deleted = ctx.Db.User.Name.Delete("Alice");
+Log.Info($"Deleted {deleted} row(s)");
+```
+
+
+
+
+```rust
+// Delete by primary key
+ctx.db.user().id().delete(123);
+
+// Delete all matching an indexed column
+let deleted = ctx.db.user().name().delete("Alice");
+log::info!("Deleted {} row(s)", deleted);
+```
+
+
+
+
+### Iterating All Rows
+
+Use `iter` to iterate over all rows in a table:
+
+
+
+
+```typescript
+for (const user of ctx.db.user.iter()) {
+ console.log(`${user.id}: ${user.name}`);
+}
+```
+
+
+
+
+```csharp
+foreach (var user in ctx.Db.User.Iter())
+{
+ Log.Info($"{user.Id}: {user.Name}");
+}
+```
+
+
+
+
+```rust
+for user in ctx.db.user().iter() {
+ log::info!("{}: {}", user.id, user.name);
+}
+```
+
+
+
+
+### Counting Rows
+
+Use `count` to get the number of rows in a table:
+
+
+
+
+```typescript
+const total = ctx.db.user.count();
+console.log(`Total users: ${total}`);
+```
+
+
+
+
+```csharp
+var total = ctx.Db.User.Count();
+Log.Info($"Total users: {total}");
+```
+
+
+
+
+```rust
+let total = ctx.db.user().count();
+log::info!("Total users: {}", total);
+```
+
+
+
-- Inserting rows
-- Updating rows by unique columns
-- Deleting rows
-- Querying with indexes
-- Iterating all rows
-- Counting rows
+For more details on querying with indexes, including range queries and multi-column indexes, see [Indexes](/tables/indexes).
## Reducer Isolation
diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md
index 75fb107c6ad..32b96e5ed33 100644
--- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md
+++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00400-reducer-context.md
@@ -15,7 +15,7 @@ The reducer context is required for accessing tables, executing database operati
The primary purpose of the reducer context is to provide access to the module's database tables.
-
+
```typescript
@@ -92,7 +92,7 @@ The context provides information about who invoked the reducer and when.
Every reducer invocation has an associated caller identity.
-
+
```typescript
@@ -213,7 +213,7 @@ The context provides access to the module's own identity, which is useful for di
This is particularly important for [scheduled reducers](/functions/reducers) that should only be invoked by the system, not by external clients.
-
+
```typescript
@@ -248,7 +248,7 @@ using SpacetimeDB;
public static partial class Module
{
- [SpacetimeDB.Table(Name = "scheduled_task", Scheduled = nameof(SendReminder))]
+ [SpacetimeDB.Table(Name = "ScheduledTask", Scheduled = nameof(SendReminder))]
public partial struct ScheduledTask
{
[SpacetimeDB.PrimaryKey]
@@ -303,7 +303,7 @@ fn send_reminder(ctx: &ReducerContext, task: ScheduledTask) {
## Context Properties Reference
-
+
| Property | Type | Description |
diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md
index 6196948ddbe..975f589b437 100644
--- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md
+++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00500-lifecycle.md
@@ -40,9 +40,9 @@ public static void Init(ReducerContext ctx)
Log.Info("Database initializing...");
// Set up default data
- if (ctx.Db.settings.Count == 0)
+ if (ctx.Db.Settings.Count == 0)
{
- ctx.Db.settings.Insert(new Settings
+ ctx.Db.Settings.Insert(new Settings
{
Key = "welcome_message",
Value = "Hello, SpacetimeDB!"
@@ -116,7 +116,7 @@ public static void OnConnect(ReducerContext ctx)
var connId = ctx.ConnectionId!.Value;
// Initialize client session
- ctx.Db.sessions.Insert(new Session
+ ctx.Db.Session.Insert(new Session
{
ConnectionId = connId,
Identity = ctx.Sender,
@@ -188,7 +188,7 @@ public static void OnDisconnect(ReducerContext ctx)
var connId = ctx.ConnectionId!.Value;
// Clean up client session
- ctx.Db.sessions.ConnectionId.Delete(connId);
+ ctx.Db.Session.ConnectionId.Delete(connId);
}
```
@@ -221,9 +221,9 @@ The `client_disconnected` reducer:
## Scheduled Reducers
-Reducers can be triggered at specific times using scheduled tables. See [Scheduled Tables](/tables/scheduled-tables) for details on:
+Reducers can be triggered at specific times using schedule tables. See [Schedule Tables](/tables/schedule-tables) for details on:
-- Defining scheduled tables
+- Defining schedule tables
- Triggering reducers at specific timestamps
- Running reducers periodically
- Canceling scheduled executions
diff --git a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md
index ea3274794d3..714f68d1110 100644
--- a/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md
+++ b/docs/docs/00200-core-concepts/00200-functions/00300-reducers/00600-error-handling.md
@@ -61,7 +61,7 @@ Throw an exception:
[SpacetimeDB.Reducer]
public static void TransferCredits(ReducerContext ctx, ulong toUser, uint amount)
{
- var fromUser = ctx.Db.users.Id.Find(ctx.Sender);
+ var fromUser = ctx.Db.User.Id.Find(ctx.Sender);
if (fromUser == null)
{
throw new InvalidOperationException("User not found");
diff --git a/docs/docs/00200-core-concepts/00200-functions/00500-views.md b/docs/docs/00200-core-concepts/00200-functions/00500-views.md
index d01ce97fbeb..97ec7e2077f 100644
--- a/docs/docs/00200-core-concepts/00200-functions/00500-views.md
+++ b/docs/docs/00200-core-concepts/00200-functions/00500-views.md
@@ -237,31 +237,96 @@ Views use one of two context types:
Both contexts provide read-only access to tables and indexes through `ctx.db`.
+### Performance: Why AnonymousViewContext Matters
+
+The choice between `ViewContext` and `AnonymousViewContext` has significant performance implications.
+
+**Anonymous views can be shared across all subscribers.** When a view uses `AnonymousViewContext`, SpacetimeDB knows the result is the same for every client. The database can materialize the view once and serve that same result to all subscribers. When the underlying data changes, it recomputes the view once and broadcasts the update to everyone.
+
+**Per-user views require separate computation for each subscriber.** When a view uses `ViewContext`, the result depends on `ctx.sender`, so each client potentially sees different data. SpacetimeDB must compute and track the view separately for each subscriber. With 1,000 connected users, that's 1,000 separate view computations and 1,000 separate sets of change tracking.
+
+**Prefer `AnonymousViewContext` when possible.** Design your views to be caller-independent when the use case allows. For example:
+
+| Use Case | Recommended Context | Why |
+|----------|-------------------|-----|
+| Global leaderboard | `AnonymousViewContext` | Same top-10 for everyone |
+| Shop inventory | `AnonymousViewContext` | Same items available to all |
+| My inventory | `ViewContext` | Different per player |
+| My messages | `ViewContext` | Private to each user |
+| World map regions | `AnonymousViewContext` | Geographic data shared by all nearby players |
+
+**Design around shared data when you can.** Sometimes a small design change lets you use anonymous views. For example, instead of a view that returns "entities near me" (which requires knowing who "me" is), consider views that return "entities in region X". Multiple players in the same region share a single materialized view rather than each having their own.
+
+### Example: Per-User View
+
+This view returns the caller's own player data. Each connected client sees different results, so SpacetimeDB must track it separately for each subscriber.
+
```typescript
-// View that depends on caller identity
+// Per-user: each client sees their own player
spacetimedb.view(
{ name: 'my_player', public: true },
t.option(players.rowType),
(ctx) => {
- const row = ctx.db.players.identity.find(ctx.sender);
- return row ?? undefined;
+ return ctx.db.players.identity.find(ctx.sender) ?? undefined;
+ }
+);
+```
+
+
+
+
+```csharp
+// Per-user: each client sees their own player
+[SpacetimeDB.View(Name = "MyPlayer", Public = true)]
+public static Player? MyPlayer(ViewContext ctx)
+{
+ return ctx.Db.Player.Identity.Find(ctx.Sender);
+}
+```
+
+
+
+
+```rust
+// Per-user: each client sees their own player
+#[view(name = my_player, public)]
+fn my_player(ctx: &ViewContext) -> Option {
+ ctx.db.player().identity().find(&ctx.sender)
+}
+```
+
+
+
+
+### Example: High Scores Leaderboard
+
+This view returns players with scores above a threshold. Every client sees the same results, so SpacetimeDB computes it once and shares it across all subscribers. The view uses a btree index on the score column to efficiently find high-scoring players.
+
+
+
+
+```typescript
+const players = table(
+ { name: 'players', public: true },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ name: t.string(),
+ score: t.u64().index('btree'),
}
);
-// View that is the same for all callers
+const spacetimedb = schema(players);
+
+// Shared: same high scorers for all clients
spacetimedb.anonymousView(
- { name: 'players_for_level', public: true },
- t.array(playerAndLevelRow),
+ { name: 'high_scorers', public: true },
+ t.array(players.rowType),
(ctx) => {
- const out: Array<{ id: bigint; name: string; level: bigint }> = [];
- for (const playerLevel of ctx.db.playerLevels.level.filter(2n)) {
- const p = ctx.db.players.id.find(playerLevel.player_id);
- if (p) out.push({ id: p.id, name: p.name, level: playerLevel.level });
- }
- return out;
+ // Get all players with score >= 1000 using the btree index
+ return Array.from(ctx.db.players.score.filter({ gte: 1000n }));
}
);
```
@@ -270,33 +335,170 @@ spacetimedb.anonymousView(
```csharp
-// View that depends on caller identity
-[SpacetimeDB.View(Name = "MyPlayer", Public = true)]
-public static Player? MyPlayer(ViewContext ctx)
+[SpacetimeDB.Table(Name = "Player", Public = true)]
+public partial struct Player
{
- return ctx.Db.Player.Identity.Find(ctx.Sender) as Player;
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ public string Name;
+ [SpacetimeDB.Index.BTree]
+ public ulong Score;
}
-// View that is the same for all callers
-[SpacetimeDB.View(Name = "PlayersForLevel", Public = true)]
-public static List PlayersForLevel(AnonymousViewContext ctx)
+// Shared: same high scorers for all clients
+[SpacetimeDB.View(Name = "HighScorers", Public = true)]
+public static List HighScorers(AnonymousViewContext ctx)
{
- var rows = new List();
- foreach (var player in ctx.Db.PlayerLevel.Level.Filter(1))
+ // Get all players with score >= 1000 using the btree index
+ return ctx.Db.Player.Score.Filter((1000, ulong.MaxValue)).ToList();
+}
+```
+
+
+
+
+```rust
+#[spacetimedb::table(name = player, public)]
+pub struct Player {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ name: String,
+ #[index(btree)]
+ score: u64,
+}
+
+// Shared: same high scorers for all clients
+#[view(name = high_scorers, public)]
+fn high_scorers(ctx: &AnonymousViewContext) -> Vec {
+ // Get all players with score >= 1000 using the btree index
+ ctx.db.player().score().filter(1000..).collect()
+}
+```
+
+
+
+
+### Example: Region-Based Design
+
+Instead of querying "what's near me" (per-user), design your data model so clients subscribe to shared regions. This example shows entities organized by chunk coordinates.
+
+
+
+
+```typescript
+const entity = table(
+ { name: 'entity', public: true },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ chunkX: t.i32().index('btree'),
+ chunkY: t.i32().index('btree'),
+ localX: t.f32(),
+ localY: t.f32(),
+ entityType: t.string(),
+ }
+);
+
+// Track which chunks each player is subscribed to
+const playerChunk = table(
+ { name: 'player_chunk', public: true },
+ {
+ playerId: t.u64().primaryKey(),
+ chunkX: t.i32(),
+ chunkY: t.i32(),
+ }
+);
+
+// Shared: all players in chunk (0,0) share this view
+spacetimedb.anonymousView(
+ { name: 'entities_in_origin_chunk', public: true },
+ t.array(entity.rowType),
+ (ctx) => {
+ // All entities in chunk (0, 0) - shared by everyone viewing this chunk
+ return Array.from(ctx.db.entity.chunkX.filter(0))
+ .filter(e => e.chunkY === 0);
+ }
+);
+
+// Per-user: returns entities in the chunk the player is currently in
+spacetimedb.view(
+ { name: 'entities_in_my_chunk', public: true },
+ t.array(entity.rowType),
+ (ctx) => {
+ const player = ctx.db.players.identity.find(ctx.sender);
+ if (!player) return [];
+
+ const chunk = ctx.db.playerChunk.playerId.find(player.id);
+ if (!chunk) return [];
+
+ return Array.from(ctx.db.entity.chunkX.filter(chunk.chunkX))
+ .filter(e => e.chunkY === chunk.chunkY);
+ }
+);
+```
+
+
+
+
+```csharp
+using SpacetimeDB;
+
+public partial class Module
+{
+ [SpacetimeDB.Table(Name = "Entity", Public = true)]
+ public partial struct Entity
{
- if (ctx.Db.Player.Id.Find(player.PlayerId) is Player p)
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ [SpacetimeDB.Index.BTree]
+ public int ChunkX;
+ [SpacetimeDB.Index.BTree]
+ public int ChunkY;
+ public float LocalX;
+ public float LocalY;
+ public string EntityType;
+ }
+
+ // Track which chunks each player is subscribed to
+ [SpacetimeDB.Table(Name = "PlayerChunk", Public = true)]
+ public partial struct PlayerChunk
+ {
+ [SpacetimeDB.PrimaryKey]
+ public ulong PlayerId;
+ public int ChunkX;
+ public int ChunkY;
+ }
+
+ // Shared: all players in chunk (0,0) share this view
+ [SpacetimeDB.View(Name = "EntitiesInOriginChunk", Public = true)]
+ public static List EntitiesInOriginChunk(AnonymousViewContext ctx)
+ {
+ // All entities in chunk (0, 0) - shared by everyone viewing this chunk
+ return ctx.Db.Entity.ChunkX.Filter(0)
+ .Where(e => e.ChunkY == 0)
+ .ToList();
+ }
+
+ // Per-user: returns entities in the chunk the player is currently in
+ [SpacetimeDB.View(Name = "EntitiesInMyChunk", Public = true)]
+ public static List EntitiesInMyChunk(ViewContext ctx)
+ {
+ if (ctx.Db.Player.Identity.Find(ctx.Sender) is not Player player)
{
- var row = new PlayerAndLevel
- {
- Id = p.Id,
- Identity = p.Identity,
- Name = p.Name,
- Level = player.Level
- };
- rows.Add(row);
+ return new List();
+ }
+
+ if (ctx.Db.PlayerChunk.PlayerId.Find(player.Id) is not PlayerChunk chunk)
+ {
+ return new List();
}
+
+ return ctx.Db.Entity.ChunkX.Filter(chunk.ChunkX)
+ .Where(e => e.ChunkY == chunk.ChunkY)
+ .ToList();
}
- return rows;
}
```
@@ -304,31 +506,50 @@ public static List PlayersForLevel(AnonymousViewContext ctx)
```rust
-// View that depends on caller identity
-#[view(name = my_player, public)]
-fn my_player(ctx: &ViewContext) -> Option {
- ctx.db.player().identity().find(ctx.sender)
+use spacetimedb::{view, AnonymousViewContext, ViewContext};
+
+#[spacetimedb::table(name = entity, public)]
+pub struct Entity {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ #[index(btree)]
+ chunk_x: i32,
+ #[index(btree)]
+ chunk_y: i32,
+ local_x: f32,
+ local_y: f32,
+ entity_type: String,
}
-// View that is the same for all callers
-#[view(name = players_for_level, public)]
-fn players_for_level(ctx: &AnonymousViewContext) -> Vec {
- ctx.db
- .player_level()
- .level()
- .filter(2u64)
- .flat_map(|player| {
- ctx.db
- .player()
- .id()
- .find(player.player_id)
- .map(|p| PlayerAndLevel {
- id: p.id,
- identity: p.identity,
- name: p.name,
- level: player.level,
- })
- })
+#[spacetimedb::table(name = player_chunk, public)]
+pub struct PlayerChunk {
+ #[primary_key]
+ player_id: u64,
+ chunk_x: i32,
+ chunk_y: i32,
+}
+
+// Shared: all players in chunk (0,0) share this view
+#[view(name = entities_in_origin_chunk, public)]
+fn entities_in_origin_chunk(ctx: &AnonymousViewContext) -> Vec {
+ ctx.db.entity().chunk_x().filter(&0)
+ .filter(|e| e.chunk_y == 0)
+ .collect()
+}
+
+// Per-user: returns entities in the chunk the player is currently in
+#[view(name = entities_in_my_chunk, public)]
+fn entities_in_my_chunk(ctx: &ViewContext) -> Vec {
+ let Some(player) = ctx.db.player().identity().find(&ctx.sender) else {
+ return vec![];
+ };
+ let Some(chunk) = ctx.db.player_chunk().player_id().find(&player.id) else {
+ return vec![];
+ };
+
+ ctx.db.entity().chunk_x().filter(&chunk.chunk_x)
+ .filter(|e| e.chunk_y == chunk.chunk_y)
.collect()
}
```
@@ -336,6 +557,10 @@ fn players_for_level(ctx: &AnonymousViewContext) -> Vec {
+The `entities_in_origin_chunk` view is shared - if 100 players are all looking at chunk (0,0), SpacetimeDB computes it once. The `entities_in_my_chunk` view requires per-user computation since each player may be in a different chunk.
+
+For games with many players in the same area, the shared approach scales much better. Clients can subscribe to the specific chunk views they need based on their position, and players in the same chunk automatically share the same materialized data.
+
## Querying Views
Views can be queried and subscribed to just like normal tables using SQL:
@@ -347,6 +572,22 @@ SELECT * FROM players_for_level;
When subscribed to, views automatically update when their underlying tables change, providing real-time updates to clients.
+## Why Views Cannot Use `.iter()`
+
+You may notice that views can only access table data through indexed lookups (`.find()` and `.filter()` on indexed columns), not through `.iter()` which scans all rows. This is a deliberate design choice for performance.
+
+**Views are black boxes.** View functions are Turing-complete code that SpacetimeDB cannot analyze or optimize. When a view reads from a table, SpacetimeDB tracks the "read set" - which rows the view accessed. If any row in that read set changes, the view's output might have changed, so SpacetimeDB must re-execute the entire view function.
+
+**Full table scans create pessimistic read sets.** If a view used `.iter()` to scan an entire table, its read set would include every row in that table. This means any change to any row - even rows unrelated to the view's output - would trigger a complete re-evaluation of the view. For a table with millions of rows, this becomes prohibitively expensive.
+
+**Index lookups enable targeted invalidation.** When a view uses `.find()` or `.filter()` on an indexed column, SpacetimeDB knows exactly which rows the view depends on. If a row outside that set changes, the view doesn't need to be re-evaluated. This keeps view updates fast and predictable.
+
+**Why SQL subscriptions can scan.** You might wonder why SQL subscription queries can include full table scans while view functions cannot. The difference is that SQL queries are not black boxes - SpacetimeDB can analyze and transform them. The query engine uses **incremental evaluation**: when rows change, it computes exactly which output rows are affected without re-running the entire query. Think of it like taking the derivative of the query - given a small change in input, compute the small change in output. Since view functions are opaque code, this kind of incremental computation isn't possible.
+
+**The tradeoff is acceptable for indexed access.** For point lookups (`.find()`) and small range scans (`.filter()` on indexed columns), the performance difference between full re-evaluation and incremental evaluation is small. This is why views are limited to indexed access - it's the subset of operations where the black-box limitation doesn't hurt performance.
+
+If you need to aggregate or sort entire tables, consider returning a `Query` from your view instead. Since queries can be analyzed by the query engine, they support incremental evaluation even when scanning full tables. Alternatively, design your schema so the data you need is accessible through indexes.
+
## Performance Considerations
Views compute results on the server side, which can improve performance by:
@@ -357,8 +598,8 @@ Views compute results on the server side, which can improve performance by:
However, keep in mind that:
-- Complex views with multiple joins or aggregations can be expensive to compute
-- Views are recomputed whenever their underlying tables change
+- Complex views with multiple joins can be expensive to compute
+- Views are recomputed when rows in their read set change
- Subscriptions to views will receive updates even if the final result doesn't change
## Next Steps
diff --git a/docs/docs/00200-core-concepts/00300-tables.md b/docs/docs/00200-core-concepts/00300-tables.md
index 1f6726f80af..c9a3405a2bc 100644
--- a/docs/docs/00200-core-concepts/00300-tables.md
+++ b/docs/docs/00200-core-concepts/00300-tables.md
@@ -9,6 +9,92 @@ import TabItem from '@theme/TabItem';
Tables are the way to store data in SpacetimeDB. All data in SpacetimeDB is stored in memory for extremely low latency and high throughput access. SpacetimeDB also automatically persists all data to disk.
+## Why Tables
+
+Tables are the fundamental unit of data organization in SpacetimeDB, just as files are the fundamental unit in Unix. However, tables possess greater generality than files. Unix requires a separate *filesystem* concept to organize and describe files. SpacetimeDB, by contrast, describes itself: it stores the representation of tables and their schemas in tables called **system tables** (such as `st_table` and `st_column`).
+
+You can query these system tables directly:
+
+```sql
+SELECT * FROM st_table;
+SELECT * FROM st_column;
+```
+
+:::warning
+You can query system tables, but you should not modify them directly. Make schema changes through the normal definition mechanisms in your module code.
+:::
+
+### Tables and Data-Oriented Design
+
+The relational model underlying tables represents the logical endpoint of [data-oriented design](https://spacetimedb.com/blog/databases-and-data-oriented-design). Patterns such as Entity Component Systems (ECS) implement a strict subset of relational capabilities. Tables give you the full power of relational theory: over fifty years of proven techniques for organizing and querying data efficiently.
+
+The central principle of data-oriented design holds that **the purpose of any program is to transform data from one form to another**. Tables provide a principled, universal representation for that data, giving you:
+
+- **Efficient access patterns** through indexes
+- **Data integrity** through constraints
+- **Flexible queries** through relational operations
+- **Real-time synchronization** through subscriptions
+
+For further discussion of this philosophy, see [The Zen of SpacetimeDB](/intro/zen).
+
+### Physical and Logical Independence
+
+A core goal of the relational model is separating *logical* access patterns from *physical* data representation. When you write a subscription query, you express *what* data you need, not *how* the database should retrieve it. This separation allows SpacetimeDB to change the physical representation of your data for performance reasons without requiring you to rewrite your queries.
+
+The clearest example is indexing. When you add an index to a column, you change how SpacetimeDB physically organizes that data. It builds an additional data structure to accelerate lookups. But your subscription queries continue to work unchanged. The same query that previously scanned the entire table now uses the index automatically. You improve performance by modifying the schema, not the queries.
+
+This independence extends beyond indexes. SpacetimeDB can change internal storage formats, memory layouts, and access algorithms across versions. Your queries remain stable because they operate at the logical level (rows and columns) rather than the physical level of bytes and pointers.
+
+### Table Decomposition
+
+A common concern when designing relational schemas is whether to consolidate data into fewer large tables or distribute it across many smaller ones. In traditional SQL databases, joins require verbose query syntax and incur significant execution cost. This friction pushes developers toward denormalized schemas with fewer, wider tables.
+
+SpacetimeDB operates under different constraints. Your reducers interact with tables through programmatic APIs rather than SQL strings. A join operation reduces to an index lookup: you retrieve a row from one table, extract a key value, and use that key to find related rows in another table. With all data resident in memory, these lookups often complete in nanoseconds.
+
+Consider the following schema for a game application:
+
+**Consolidated approach (not recommended):**
+
+```
+Player
+├── id
+├── name
+├── position_x, position_y, velocity_x, velocity_y (updates: 60Hz)
+├── health, max_health, mana, max_mana (updates: occasional)
+├── total_kills, total_deaths, play_time (updates: rare)
+└── audio_volume, graphics_quality (updates: very rare)
+```
+
+**Decomposed approach (recommended):**
+
+```
+Player PlayerState PlayerStats PlayerSettings
+├── id ←── ├── player_id ├── player_id ├── player_id
+└── name ├── position_x ├── total_kills ├── audio_volume
+ ├── position_y ├── total_deaths └── graphics_quality
+ ├── velocity_x └── play_time
+ └── velocity_y
+
+PlayerResources
+├── player_id
+├── health
+├── max_health
+├── mana
+└── max_mana
+```
+
+The decomposed approach yields several advantages:
+
+1. **Reduced bandwidth**: Clients subscribing to player positions do not receive updates when settings change. For an application with 1000 concurrent players updating positions at 60Hz, this reduction is substantial.
+
+2. **Cache efficiency**: Data with similar update frequencies resides in contiguous memory. Updating a player's position does not require loading or invalidating cache lines containing lifetime statistics.
+
+3. **Semantic clarity**: Each table maintains a single responsibility. `PlayerState` handles the performance-critical gameplay loop. `PlayerStats` serves leaderboard queries. `PlayerSettings` supports the options interface.
+
+4. **Schema evolution**: You can add columns to `PlayerStats` without affecting the structure or performance characteristics of `PlayerState`.
+
+The guiding principle: **organize data by access pattern, not by the entity it describes**. Keep data you read together in the same table. Separate data you read at different times or frequencies.
+
## Defining Tables
Tables are defined in your module code with a name, columns, and optional configuration.
@@ -39,8 +125,8 @@ The first argument defines table options, and the second defines columns.
Use the `[SpacetimeDB.Table]` attribute on a `partial struct` or `partial class`:
```csharp
-[SpacetimeDB.Table(Name = "people", Public = true)]
-public partial struct People
+[SpacetimeDB.Table(Name = "Person", Public = true)]
+public partial struct Person
{
[SpacetimeDB.PrimaryKey]
[SpacetimeDB.AutoInc]
@@ -62,8 +148,8 @@ The `partial` modifier is required to allow code generation.
Use the `#[spacetimedb::table]` macro on a struct:
```rust
-#[spacetimedb::table(name = people, public)]
-pub struct People {
+#[spacetimedb::table(name = person, public)]
+pub struct Person {
#[primary_key]
#[auto_inc]
id: u32,
@@ -77,6 +163,93 @@ pub struct People {
+## Table Naming and Accessors
+
+The table name you specify determines how you access the table in your code. Understanding this relationship is essential for writing correct SpacetimeDB modules.
+
+### How Accessor Names Are Derived
+
+
+
+
+The accessor name is converted from snake_case to camelCase:
+
+```typescript
+// Table definition
+const player_scores = table(
+ { name: 'player_scores', public: true },
+ { /* columns */ }
+);
+
+// Accessor uses camelCase
+ctx.db.playerScores.insert({ /* ... */ });
+```
+
+| Table Name | Accessor |
+|------------|----------|
+| `'user'` | `ctx.db.user` |
+| `'player_scores'` | `ctx.db.playerScores` |
+| `'game_session'` | `ctx.db.gameSession` |
+
+
+
+
+The accessor name **exactly matches** the `Name` attribute value:
+
+```csharp
+// Table definition
+[SpacetimeDB.Table(Name = "Player", Public = true)]
+public partial struct Player { /* columns */ }
+
+// Accessor matches Name exactly
+ctx.Db.Player.Insert(new Player { /* ... */ });
+```
+
+| Name Attribute | Accessor |
+|----------------|----------|
+| `Name = "User"` | `ctx.Db.User` |
+| `Name = "Player"` | `ctx.Db.Player` |
+| `Name = "GameSession"` | `ctx.Db.GameSession` |
+
+:::warning Case Sensitivity
+The accessor is case-sensitive and must match the `Name` value exactly. `Name = "user"` produces `ctx.Db.user`, not `ctx.Db.User`.
+:::
+
+
+
+
+The accessor name **exactly matches** the `name` attribute value:
+
+```rust
+// Table definition
+#[spacetimedb::table(name = player, public)]
+pub struct Player { /* columns */ }
+
+// Accessor matches name exactly
+ctx.db.player().insert(Player { /* ... */ });
+```
+
+| name Attribute | Accessor |
+|----------------|----------|
+| `name = user` | `ctx.db.user()` |
+| `name = player` | `ctx.db.player()` |
+| `name = game_session` | `ctx.db.game_session()` |
+
+
+
+
+### Recommended Naming Conventions
+
+Use idiomatic naming conventions for each language:
+
+| Language | Convention | Example Table | Example Accessor |
+|----------|------------|---------------|------------------|
+| **TypeScript** | snake_case | `'player_score'` | `ctx.db.playerScore` |
+| **C#** | PascalCase | `Name = "PlayerScore"` | `ctx.Db.PlayerScore` |
+| **Rust** | lower_snake_case | `name = player_score` | `ctx.db.player_score()` |
+
+These conventions align with each language's standard style guides and make your code feel natural within its ecosystem.
+
## Table Visibility
Tables can be **private** (default) or **public**:
@@ -96,10 +269,10 @@ const privateTable = table({ name: 'secret', public: false }, { /* ... */ });
```csharp
-[SpacetimeDB.Table(Name = "user", Public = true)]
+[SpacetimeDB.Table(Name = "User", Public = true)]
public partial struct User { /* ... */ }
-[SpacetimeDB.Table(Name = "secret", Public = false)]
+[SpacetimeDB.Table(Name = "Secret", Public = false)]
public partial struct Secret { /* ... */ }
```
@@ -117,10 +290,141 @@ pub struct Secret { /* ... */ }
+For more fine-grained access control, you can use [view functions](/functions/views) to expose computed subsets of your data to clients. Views allow you to filter rows, select specific columns, or join data from multiple tables before exposing it.
+
+See [Access Permissions](/tables/access-permissions) for complete details on table visibility and access patterns.
+
+## Multiple Tables for the Same Type
+
+You can create multiple tables that share the same row type by applying multiple table attributes to a single struct. Each table stores its own independent set of rows, but all tables share the same schema.
+
+
+
+
+In TypeScript, define separate table variables that share the same column schema:
+
+```typescript
+import { table, t } from 'spacetimedb/server';
+
+// Define the shared column schema
+const playerColumns = {
+ identity: t.Identity.primaryKey(),
+ playerId: t.i32().unique().autoInc(),
+ name: t.string(),
+};
+
+// Create two tables with the same schema
+const Player = table({ name: 'Player', public: true }, playerColumns);
+const LoggedOutPlayer = table({ name: 'LoggedOutPlayer' }, playerColumns);
+```
+
+
+
+
+Apply multiple `[Table]` attributes to the same struct:
+
+```csharp
+[SpacetimeDB.Table(Name = "Player", Public = true)]
+[SpacetimeDB.Table(Name = "LoggedOutPlayer")]
+public partial struct Player
+{
+ [PrimaryKey]
+ public Identity Identity;
+ [Unique, AutoInc]
+ public int PlayerId;
+ public string Name;
+}
+```
+
+Each table gets its own accessor:
+
+```csharp
+// Insert into different tables
+ctx.Db.Player.Insert(new Player { /* ... */ });
+ctx.Db.LoggedOutPlayer.Insert(new Player { /* ... */ });
+
+// Move a row between tables
+var player = ctx.Db.LoggedOutPlayer.Identity.Find(ctx.Sender);
+if (player != null)
+{
+ ctx.Db.Player.Insert(player.Value);
+ ctx.Db.LoggedOutPlayer.Identity.Delete(player.Value.Identity);
+}
+```
+
+
+
+
+Apply multiple `#[spacetimedb::table]` attributes to the same struct:
+
+```rust
+#[spacetimedb::table(name = player, public)]
+#[spacetimedb::table(name = logged_out_player)]
+pub struct Player {
+ #[primary_key]
+ identity: Identity,
+ #[unique]
+ #[auto_inc]
+ player_id: i32,
+ name: String,
+}
+```
+
+Each table gets its own accessor:
+
+```rust
+// Insert into different tables
+ctx.db.player().insert(Player { /* ... */ });
+ctx.db.logged_out_player().insert(Player { /* ... */ });
+
+// Move a row between tables
+if let Some(player) = ctx.db.logged_out_player().identity().find(&ctx.sender) {
+ ctx.db.player().insert(player.clone());
+ ctx.db.logged_out_player().identity().delete(&player.identity);
+}
+```
+
+
+
+
+This pattern is useful for:
+
+- **State management**: Separate active users from inactive users, online players from offline players
+- **Archiving**: Move old records to an archive table while keeping the same schema
+- **Staging**: Hold pending records in one table before moving them to a main table
+
+:::note Shared Constraints
+Column attributes like `[PrimaryKey]`, `[Unique]`, `[AutoInc]`, and `[Index]` apply to **all tables** defined on the type. Each table will have its own independent primary key, unique constraints, and indexes with the same structure.
+:::
+
+## Constraints
+
+Tables support several constraints to enforce data integrity:
+
+- **Primary keys** uniquely identify each row and define how updates and deletes work
+- **Unique constraints** ensure no two rows share the same value for a column
+
+See [Constraints](/tables/constraints) for details.
+
+## Auto-Increment
+
+Auto-increment columns automatically generate unique integer values for new rows. SpacetimeDB implements auto-increment using sequences, which provide crash-safe value generation with configurable parameters.
+
+See [Auto-Increment](/tables/auto-increment) for details.
+
+## Schedule Tables
+
+Tables can trigger reducers at specific times by including a scheduling column. This allows you to schedule future actions like sending reminders, expiring content, or running periodic maintenance.
+
+See [Schedule Tables](/tables/schedule-tables) for details.
+
## Next Steps
-- [Column Types and Constraints](/tables/columns) - Define table structure with types, unique columns, primary keys, and auto-increment
+- [Column Types](/tables/column-types) - Supported column types and performance considerations
+- [Constraints](/tables/constraints) - Primary keys and unique constraints
+- [Auto-Increment](/tables/auto-increment) - Automatic ID generation with sequences
+- [Default Values](/tables/default-values) - Schema evolution with column defaults
- [Indexes](/tables/indexes) - Speed up queries with single and multi-column indexes
-- [Access Permissions](/tables/access-permissions) - Query and modify tables from reducers, views, and clients
-- Learn about [Scheduling tables](/tables/scheduled-tables)
-- Learn about [Performance Best Practices](/tables/performance)
+- [Access Permissions](/tables/access-permissions) - Public vs private tables
+- [Schedule Tables](/tables/schedule-tables) - Time-based reducer execution
+- [Performance](/tables/performance) - Best practices for table design
diff --git a/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md b/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md
new file mode 100644
index 00000000000..853a55466b9
--- /dev/null
+++ b/docs/docs/00200-core-concepts/00300-tables/00200-column-types.md
@@ -0,0 +1,293 @@
+---
+title: Column Types
+slug: /tables/column-types
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+Columns define the structure of your tables. SpacetimeDB supports primitive types, composite types for complex data, and special types for database-specific functionality.
+
+## Representing Collections
+
+When modeling data that contains multiple items, you have two choices: store the collection as a column (using `Vec`, `List`, or `Array`) or store each item as a row in a separate table. This decision affects how you query, update, and subscribe to that data.
+
+**Use a collection column when:**
+- The items form an atomic unit that you always read and write together
+- Order is semantically important and frequently accessed by position
+- The collection is small and bounded (e.g., a fixed-size inventory)
+- The items are values without independent identity
+
+**Use a separate table when:**
+- Items have independent identity and lifecycle
+- You need to query, filter, or index individual items
+- The collection can grow unbounded
+- Clients should receive updates for individual item changes, not the entire collection
+- You want to enforce referential integrity between items and other data
+
+Consider a game inventory with ordered pockets. A `Vec- ` preserves pocket order naturally, but if you need to query "all items owned by player X" across multiple players, a separate `inventory_item` table with a `pocket_index` column allows that query efficiently. The right choice depends on your dominant access patterns.
+
+## Binary Data and Files
+
+SpacetimeDB includes optimizations for storing binary data as `Vec` (Rust), `List` (C#), or `t.array(t.u8())` (TypeScript). You can store files, images, serialized data, or other binary blobs directly in table columns.
+
+This approach works well when:
+- The binary data is associated with a specific row (e.g., a user's avatar image)
+- You want the data to participate in transactions and subscriptions
+- The data size is reasonable (up to several megabytes per row)
+
+For very large files or data that changes independently of other row fields, consider external storage with a reference stored in the table.
+
+## Type Performance
+
+SpacetimeDB optimizes reading and writing by taking advantage of memory layout. Several factors affect performance:
+
+**Prefer smaller types.** Use the smallest integer type that fits your data range. A `u8` storing values 0-255 uses less memory and bandwidth than a `u64` storing the same values. This reduces storage, speeds up serialization, and improves cache efficiency.
+
+**Prefer fixed-size types.** Fixed-size types (`u32`, `f64`, fixed-size structs) allow SpacetimeDB to compute memory offsets directly. Variable-size types (`String`, `Vec`) require additional indirection. When performance matters, consider fixed-size alternatives:
+- Use `[u8; 32]` instead of `Vec` for fixed-length hashes or identifiers
+- Use an enum with a fixed set of variants instead of a `String` for categorical data
+
+**Consider column ordering.** Types require alignment in memory. A `u64` aligns to 8-byte boundaries, while a `u8` aligns to 1-byte boundaries. When smaller types precede larger ones, the compiler may insert padding bytes to satisfy alignment requirements. Ordering columns from largest to smallest alignment can reduce padding and improve memory density.
+
+For example, a struct with fields `(u8, u64, u8)` may require 24 bytes due to padding, while `(u64, u8, u8)` requires only 16 bytes. This optimization is not something to follow religiously, but it can help performance in memory-intensive scenarios.
+
+These optimizations apply across all supported languages.
+
+## Type Reference
+
+
+
+
+| Category | Type | TypeScript Type | Description |
+|----------|------|-----------------|-------------|
+| Primitive | `t.bool()` | `boolean` | Boolean value |
+| Primitive | `t.string()` | `string` | UTF-8 string |
+| Primitive | `t.f32()` | `number` | 32-bit floating point |
+| Primitive | `t.f64()` | `number` | 64-bit floating point |
+| Primitive | `t.i8()` | `number` | Signed 8-bit integer |
+| Primitive | `t.u8()` | `number` | Unsigned 8-bit integer |
+| Primitive | `t.i16()` | `number` | Signed 16-bit integer |
+| Primitive | `t.u16()` | `number` | Unsigned 16-bit integer |
+| Primitive | `t.i32()` | `number` | Signed 32-bit integer |
+| Primitive | `t.u32()` | `number` | Unsigned 32-bit integer |
+| Primitive | `t.i64()` | `bigint` | Signed 64-bit integer |
+| Primitive | `t.u64()` | `bigint` | Unsigned 64-bit integer |
+| Primitive | `t.i128()` | `bigint` | Signed 128-bit integer |
+| Primitive | `t.u128()` | `bigint` | Unsigned 128-bit integer |
+| Primitive | `t.i256()` | `bigint` | Signed 256-bit integer |
+| Primitive | `t.u256()` | `bigint` | Unsigned 256-bit integer |
+| Composite | `t.object(name, obj)` | `{ [K in keyof Obj]: T }` | Product/object type for nested data |
+| Composite | `t.enum(name, variants)` | `{ tag: 'variant' } \| { tag: 'variant', value: T }` | Sum/enum type (tagged union) |
+| Composite | `t.array(element)` | `T[]` | Array of elements |
+| Composite | `t.option(value)` | `Value \| undefined` | Optional value |
+| Composite | `t.unit()` | `{}` | Zero-field product type |
+| Special | `t.identity()` | `Identity` | Unique identity for authentication |
+| Special | `t.connectionId()` | `ConnectionId` | Client connection identifier |
+| Special | `t.timestamp()` | `Timestamp` | Absolute point in time (microseconds since Unix epoch) |
+| Special | `t.timeDuration()` | `TimeDuration` | Relative duration in microseconds |
+| Special | `t.scheduleAt()` | `ScheduleAt` | Column type for scheduling reducer execution |
+
+
+
+
+| Category | Type | Description |
+|----------|------|-------------|
+| Primitive | `bool` | Boolean value |
+| Primitive | `string` | UTF-8 string |
+| Primitive | `float` | 32-bit floating point |
+| Primitive | `double` | 64-bit floating point |
+| Primitive | `sbyte`, `short`, `int`, `long` | Signed integers (8-bit to 64-bit) |
+| Primitive | `byte`, `ushort`, `uint`, `ulong` | Unsigned integers (8-bit to 64-bit) |
+| Primitive | `SpacetimeDB.I128`, `SpacetimeDB.I256` | Signed 128-bit and 256-bit integers |
+| Primitive | `SpacetimeDB.U128`, `SpacetimeDB.U256` | Unsigned 128-bit and 256-bit integers |
+| Composite | `struct` with `[SpacetimeDB.Type]` | Product type for nested data |
+| Composite | `TaggedEnum` | Sum type (tagged union) |
+| Composite | `List` | List of elements |
+| Composite | `T?` | Nullable/optional value |
+| Special | `Identity` | Unique identity for authentication |
+| Special | `ConnectionId` | Client connection identifier |
+| Special | `Timestamp` | Absolute point in time (microseconds since Unix epoch) |
+| Special | `TimeDuration` | Relative duration in microseconds |
+| Special | `ScheduleAt` | When a scheduled reducer should execute |
+
+
+
+
+| Category | Type | Description |
+|----------|------|-------------|
+| Primitive | `bool` | Boolean value |
+| Primitive | `String` | UTF-8 string |
+| Primitive | `f32`, `f64` | Floating point numbers |
+| Primitive | `i8`, `i16`, `i32`, `i64`, `i128` | Signed integers |
+| Primitive | `u8`, `u16`, `u32`, `u64`, `u128` | Unsigned integers |
+| Composite | `struct` with `#[derive(SpacetimeType)]` | Product type for nested data |
+| Composite | `enum` with `#[derive(SpacetimeType)]` | Sum type (tagged union) |
+| Composite | `Vec` | Vector of elements |
+| Composite | `Option` | Optional value |
+| Special | `Identity` | Unique identity for authentication |
+| Special | `ConnectionId` | Client connection identifier |
+| Special | `Timestamp` | Absolute point in time (microseconds since Unix epoch) |
+| Special | `Duration` | Relative duration |
+| Special | `ScheduleAt` | When a scheduled reducer should execute |
+
+
+
+
+## Complete Example
+
+The following example demonstrates a table using primitive, composite, and special types:
+
+
+
+
+```typescript
+import { table, t } from 'spacetimedb/server';
+
+// Define a nested object type for coordinates
+const Coordinates = t.object('Coordinates', {
+ x: t.f64(),
+ y: t.f64(),
+ z: t.f64(),
+});
+
+// Define an enum for status
+const Status = t.enum('Status', {
+ Active: t.unit(),
+ Inactive: t.unit(),
+ Suspended: t.object('SuspendedInfo', { reason: t.string() }),
+});
+
+const player = table(
+ { name: 'player', public: true },
+ {
+ // Primitive types
+ id: t.u64().primaryKey().autoInc(),
+ name: t.string(),
+ level: t.u8(),
+ experience: t.u32(),
+ health: t.f32(),
+ score: t.i64(),
+ is_online: t.bool(),
+
+ // Composite types
+ position: Coordinates,
+ status: Status,
+ inventory: t.array(t.u32()),
+ guild_id: t.option(t.u64()),
+
+ // Special types
+ owner: t.identity(),
+ connection: t.option(t.connectionId()),
+ created_at: t.timestamp(),
+ play_time: t.timeDuration(),
+ }
+);
+```
+
+
+
+
+```csharp
+using SpacetimeDB;
+
+public static partial class Module
+{
+ // Define a nested struct type for coordinates
+ [SpacetimeDB.Type]
+ public partial struct Coordinates
+ {
+ public double X;
+ public double Y;
+ public double Z;
+ }
+
+ // Define an enum for status
+ [SpacetimeDB.Type]
+ public partial record Status : TaggedEnum<(
+ Unit Active,
+ Unit Inactive,
+ string Suspended
+ )> { }
+
+ [SpacetimeDB.Table(Name = "Player", Public = true)]
+ public partial struct Player
+ {
+ // Primitive types
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ public string Name;
+ public byte Level;
+ public uint Experience;
+ public float Health;
+ public long Score;
+ public bool IsOnline;
+
+ // Composite types
+ public Coordinates Position;
+ public Status Status;
+ public List Inventory;
+ public ulong? GuildId;
+
+ // Special types
+ public Identity Owner;
+ public ConnectionId? Connection;
+ public Timestamp CreatedAt;
+ public TimeDuration PlayTime;
+ }
+}
+```
+
+
+
+
+```rust
+use spacetimedb::{SpacetimeType, Identity, ConnectionId, Timestamp, TimeDuration};
+
+// Define a nested struct type for coordinates
+#[derive(SpacetimeType)]
+pub struct Coordinates {
+ x: f64,
+ y: f64,
+ z: f64,
+}
+
+// Define an enum for status
+#[derive(SpacetimeType)]
+pub enum Status {
+ Active,
+ Inactive,
+ Suspended { reason: String },
+}
+
+#[spacetimedb::table(name = player, public)]
+pub struct Player {
+ // Primitive types
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ name: String,
+ level: u8,
+ experience: u32,
+ health: f32,
+ score: i64,
+ is_online: bool,
+
+ // Composite types
+ position: Coordinates,
+ status: Status,
+ inventory: Vec,
+ guild_id: Option,
+
+ // Special types
+ owner: Identity,
+ connection: Option,
+ created_at: Timestamp,
+ play_time: TimeDuration,
+}
+```
+
+
+
diff --git a/docs/docs/00200-core-concepts/00300-tables/00200-columns.md b/docs/docs/00200-core-concepts/00300-tables/00200-columns.md
deleted file mode 100644
index 5fe338ecc6e..00000000000
--- a/docs/docs/00200-core-concepts/00300-tables/00200-columns.md
+++ /dev/null
@@ -1,386 +0,0 @@
----
-title: Column Types and Constraints
-slug: /tables/columns
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-
-Columns define the structure of your tables. Each column has a type and can have constraints that enforce data integrity.
-
-## Column Types
-
-SpacetimeDB supports a variety of column types optimized for performance.
-
-### Primitive Types
-
-
-
-
-| Type | Returns | TypeScript Type | Description |
-|------|---------|----------------|-------------|
-| `t.bool()` | `BoolBuilder` | `boolean` | Boolean value |
-| `t.string()` | `StringBuilder` | `string` | UTF-8 string |
-| `t.f32()` | `F32Builder` | `number` | 32-bit floating point |
-| `t.f64()` | `F64Builder` | `number` | 64-bit floating point |
-| `t.i8()` | `I8Builder` | `number` | Signed 8-bit integer |
-| `t.u8()` | `U8Builder` | `number` | Unsigned 8-bit integer |
-| `t.i16()` | `I16Builder` | `number` | Signed 16-bit integer |
-| `t.u16()` | `U16Builder` | `number` | Unsigned 16-bit integer |
-| `t.i32()` | `I32Builder` | `number` | Signed 32-bit integer |
-| `t.u32()` | `U32Builder` | `number` | Unsigned 32-bit integer |
-| `t.i64()` | `I64Builder` | `bigint` | Signed 64-bit integer |
-| `t.u64()` | `U64Builder` | `bigint` | Unsigned 64-bit integer |
-| `t.i128()` | `I128Builder` | `bigint` | Signed 128-bit integer |
-| `t.u128()` | `U128Builder` | `bigint` | Unsigned 128-bit integer |
-| `t.i256()` | `I256Builder` | `bigint` | Signed 256-bit integer |
-| `t.u256()` | `U256Builder` | `bigint` | Unsigned 256-bit integer |
-
-
-
-
-| Type | Description |
-|------|-------------|
-| `bool` | Boolean value |
-| `string` | UTF-8 string |
-| `float`, `double` | Floating point numbers |
-| `sbyte`, `short`, `int`, `long` | Signed integers (8-bit to 64-bit) |
-| `byte`, `ushort`, `uint`, `ulong` | Unsigned integers (8-bit to 64-bit) |
-| `SpacetimeDB.I128`, `SpacetimeDB.I256` | Signed 128-bit and 256-bit integers |
-| `SpacetimeDB.U128`, `SpacetimeDB.U256` | Unsigned 128-bit and 256-bit integers |
-
-
-
-
-| Type | Description |
-|------|-------------|
-| `bool` | Boolean value |
-| `String` | UTF-8 string |
-| `f32`, `f64` | Floating point numbers |
-| `i8` through `i128` | Signed integers |
-| `u8` through `u128` | Unsigned integers |
-
-
-
-
-### Special Types
-
-
-
-
-**Structured Types**
-
-| Type | Returns | TypeScript Type | Description |
-|------|---------|----------------|-------------|
-| `t.object(name, obj)` | `ProductBuilder` | `{ [K in keyof Obj]: T }` | Product/object type for nested or structured data |
-| `t.row(obj)` | `RowBuilder` | `{ [K in keyof Obj]: T }` | Row type for table schemas (allows column metadata) |
-| `t.enum(name, variants)` | `SumBuilder` or `SimpleSumBuilder` | `{ tag: 'variant' } \| { tag: 'variant', value: T }` | Sum/enum type (tagged union or simple enum) |
-| `t.array(element)` | `ArrayBuilder` | `T[]` | Array of the given element type |
-| `t.unit()` | `UnitBuilder` | `{}` or `undefined` | Zero-field product type (unit) |
-| `t.option(value)` | `OptionBuilder` | `Value \| undefined` | Optional value type |
-
-**Special Types**
-
-| Type | Returns | TypeScript Type | Description |
-|------|---------|----------------|-------------|
-| `t.identity()` | `IdentityBuilder` | `Identity` | Unique identity for authentication |
-| `t.connectionId()` | `ConnectionIdBuilder` | `ConnectionId` | Client connection identifier |
-| `t.timestamp()` | `TimestampBuilder` | `Timestamp` | Absolute point in time (microseconds since Unix epoch) |
-| `t.timeDuration()` | `TimeDurationBuilder` | `TimeDuration` | Relative duration in microseconds |
-| `t.scheduleAt()` | `ColumnBuilder` | `ScheduleAt` | Special column type for scheduling reducer execution |
-
-
-
-
-**Structured Types**
-
-| Type | Description |
-|------|-------------|
-| `TaggedEnum` | Tagged union/enum type for sum types |
-| `T?` | Nullable/optional value |
-| `List` | List of elements |
-
-**Special Types**
-
-| Type | Description |
-|------|-------------|
-| `Identity` | Unique identity for authentication |
-| `ConnectionId` | Client connection identifier |
-| `Timestamp` | Absolute point in time (microseconds since Unix epoch) |
-| `TimeDuration` | Relative duration in microseconds |
-| `ScheduleAt` | When a scheduled reducer should execute (either at a specific time or at repeating intervals) |
-
-
-
-
-**Structured Types**
-
-| Type | Description |
-|------|-------------|
-| `enum` with `#[derive(SpacetimeType)]` | Sum type/tagged union |
-| `Option` | Optional value |
-| `Vec` | Vector of elements |
-
-**Special Types**
-
-| Type | Description |
-|------|-------------|
-| `Identity` | Unique identity for authentication |
-| `ConnectionId` | Client connection identifier |
-| `Timestamp` | Absolute point in time (microseconds since Unix epoch) |
-| `Duration` | Relative duration |
-| `ScheduleAt` | When a scheduled reducer should execute (either `Time(Timestamp)` or `Interval(Duration)`) |
-
-
-
-
-## Column Constraints
-
-### Unique Columns
-
-Mark columns as unique to ensure only one row can exist with a given value.
-
-
-
-
-```typescript
-const user = table(
- { name: 'user', public: true },
- {
- id: t.u32().primaryKey(),
- email: t.string().unique(),
- username: t.string().unique(),
- }
-);
-```
-
-
-
-
-```csharp
-[SpacetimeDB.Table(Name = "user", Public = true)]
-public partial struct User
-{
- [SpacetimeDB.PrimaryKey]
- public uint Id;
-
- [SpacetimeDB.Unique]
- public string Email;
-
- [SpacetimeDB.Unique]
- public string Username;
-}
-```
-
-
-
-
-```rust
-#[spacetimedb::table(name = user, public)]
-pub struct User {
- #[primary_key]
- id: u32,
- #[unique]
- email: String,
- #[unique]
- username: String,
-}
-```
-
-
-
-
-### Primary Key
-
-A table can have one primary key column. The primary key represents the identity of the row. Changes that don't affect the primary key are updates; changes to the primary key are treated as delete + insert.
-
-Only one column can be marked as a primary key, but multiple columns can be marked unique.
-
-### Auto-Increment Columns
-
-Use auto-increment for automatically increasing integer identifiers. Inserting a row with a zero value causes the database to assign a new unique value.
-
-
-
-
-```typescript
-const post = table(
- { name: 'post', public: true },
- {
- id: t.u64().primaryKey().autoInc(),
- title: t.string(),
- }
-);
-
-const spacetimedb = schema(post);
-
-spacetimedb.reducer('add_post', { title: t.string() }, (ctx, { title }) => {
- const inserted = ctx.db.post.insert({ id: 0, title });
- // inserted.id now contains the assigned auto-incremented value
-});
-```
-
-
-
-
-```csharp
-[SpacetimeDB.Table(Name = "post", Public = true)]
-public partial struct Post
-{
- [SpacetimeDB.PrimaryKey]
- [SpacetimeDB.AutoInc]
- public ulong Id;
- public string Title;
-}
-
-[SpacetimeDB.Reducer]
-public static void AddPost(ReducerContext ctx, string title)
-{
- var inserted = ctx.Db.post.Insert(new Post { Id = 0, Title = title });
- // inserted.Id now contains the assigned auto-incremented value
-}
-```
-
-
-
-
-```rust
-#[spacetimedb::table(name = post, public)]
-pub struct Post {
- #[primary_key]
- #[auto_inc]
- id: u64,
- title: String,
-}
-
-#[spacetimedb::reducer]
-fn add_post(ctx: &ReducerContext, title: String) -> Result<(), String> {
- let inserted = ctx.db.post().insert(Post { id: 0, title })?;
- // inserted.id now contains the assigned auto-incremented value
- Ok(())
-}
-```
-
-
-
-
-### Default Values
-
-Default values allow you to add new columns to existing tables during [automatic migrations](/databases/automatic-migrations). When you republish a module with a new column that has a default value, existing rows are automatically populated with that default.
-
-:::note
-New columns with default values must be added at the **end** of the table definition. Adding columns in the middle of a table is not supported.
-:::
-
-
-
-
-```typescript
-const player = table(
- { name: 'player', public: true },
- {
- id: t.u64().primaryKey().autoInc(),
- name: t.string(),
- // New columns added with defaults
- score: t.u32().default(0),
- isActive: t.bool().default(true),
- bio: t.string().default(''),
- }
-);
-```
-
-The `.default(value)` method can be chained on any column type builder. The value must match the column's type.
-
-
-
-
-```csharp
-[SpacetimeDB.Table(Name = "player", Public = true)]
-public partial struct Player
-{
- [SpacetimeDB.PrimaryKey]
- [SpacetimeDB.AutoInc]
- public ulong Id;
-
- public string Name;
-
- // New columns added with defaults
- [SpacetimeDB.Default(0u)]
- public uint Score;
-
- [SpacetimeDB.Default(true)]
- public bool IsActive;
-
- [SpacetimeDB.Default("")]
- public string Bio;
-}
-```
-
-The `[SpacetimeDB.Default(value)]` attribute specifies the default value. The value is serialized to match the column's type.
-
-
-
-
-```rust
-#[spacetimedb::table(name = player, public)]
-pub struct Player {
- #[primary_key]
- #[auto_inc]
- id: u64,
- name: String,
- // New columns added with defaults
- #[default(0)]
- score: u32,
- #[default(true)]
- is_active: bool,
-}
-```
-
-The `#[default(value)]` attribute specifies the default value. The expression must be const-evaluable (usable in a `const` context).
-
-:::note Rust Limitation
-Default values in Rust must be const-evaluable. This means you **cannot** use `String` defaults like `#[default("".to_string())]` because `.to_string()` is not a const fn. Only primitive types, enums, and other const-constructible types can have defaults.
-:::
-
-
-
-
-#### Constraints
-
-Default values **cannot** be combined with:
-- `#[primary_key]` / `[PrimaryKey]` / `.primaryKey()`
-- `#[unique]` / `[Unique]` / `.unique()`
-- `#[auto_inc]` / `[AutoInc]` / `.autoInc()`
-
-This restriction exists because these constraints require the database to manage the column values, which conflicts with providing a static default.
-
-:::warning TypeScript: Unintuitive Error Message
-
-In TypeScript, this constraint is enforced at compile time. If you violate it, you'll see an **unintuitive error message**:
-
-```
-Expected 3 arguments, but got 2.
-```
-
-**This error means one of your columns has an invalid combination of `.default()` with `.primaryKey()`, `.unique()`, or `.autoInc()`.**
-
-For example, this code will produce the error:
-
-```typescript
-// ERROR: default() + primaryKey() is not allowed
-const badTable = table(
- { name: 'bad' },
- { id: t.u64().default(0n).primaryKey() } // <- Causes "Expected 3 arguments"
-);
-```
-
-**How to fix:** Remove either `.default()` or the constraint (`.primaryKey()`/`.unique()`/`.autoInc()`).
-
-:::
-
-#### Use Cases
-
-- **Schema evolution**: Add new features to your application without losing existing data
-- **Optional fields**: Provide sensible defaults for fields that may not have been tracked historically
-- **Feature flags**: Add boolean columns with `default(false)` to enable new functionality gradually
diff --git a/docs/docs/00200-core-concepts/00300-tables/00230-auto-increment.md b/docs/docs/00200-core-concepts/00300-tables/00230-auto-increment.md
new file mode 100644
index 00000000000..7ba9ab238d0
--- /dev/null
+++ b/docs/docs/00200-core-concepts/00300-tables/00230-auto-increment.md
@@ -0,0 +1,244 @@
+---
+title: Auto-Increment
+slug: /tables/auto-increment
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+Auto-increment columns automatically generate unique integer values for new rows. When you insert a row with a zero value in an auto-increment column, SpacetimeDB assigns the next value from an internal sequence.
+
+## Defining Auto-Increment Columns
+
+
+
+
+```typescript
+const post = table(
+ { name: 'post', public: true },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ title: t.string(),
+ }
+);
+
+const spacetimedb = schema(post);
+
+spacetimedb.reducer('add_post', { title: t.string() }, (ctx, { title }) => {
+ // Pass 0 for the auto-increment field
+ const inserted = ctx.db.post.insert({ id: 0n, title });
+ // inserted.id now contains the assigned value
+ console.log(`Created post with id: ${inserted.id}`);
+});
+```
+
+Use the `.autoInc()` method on a column builder.
+
+
+
+
+```csharp
+[SpacetimeDB.Table(Name = "Post", Public = true)]
+public partial struct Post
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ public string Title;
+}
+
+[SpacetimeDB.Reducer]
+public static void AddPost(ReducerContext ctx, string title)
+{
+ // Pass 0 for the auto-increment field
+ var inserted = ctx.Db.Post.Insert(new Post { Id = 0, Title = title });
+ // inserted.Id now contains the assigned value
+ Log.Info($"Created post with id: {inserted.Id}");
+}
+```
+
+Use the `[SpacetimeDB.AutoInc]` attribute.
+
+
+
+
+```rust
+use spacetimedb::{ReducerContext, Table};
+
+#[spacetimedb::table(name = post, public)]
+pub struct Post {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ title: String,
+}
+
+#[spacetimedb::reducer]
+fn add_post(ctx: &ReducerContext, title: String) -> Result<(), String> {
+ // Pass 0 for the auto-increment field
+ let inserted = ctx.db.post().insert(Post { id: 0, title });
+ // inserted.id now contains the assigned value
+ log::info!("Created post with id: {}", inserted.id);
+ Ok(())
+}
+```
+
+Use the `#[auto_inc]` attribute.
+
+
+
+
+Auto-increment columns must be integer types (`u8`, `u16`, `u32`, `u64`, `i8`, `i16`, `i32`, `i64`, etc.).
+
+## Trigger Value
+
+The auto-increment mechanism activates when you insert a row with a **zero value** in the auto-increment column. If you insert a non-zero value, SpacetimeDB uses that value directly without generating a new one.
+
+```rust
+// Triggers auto-increment: id will be assigned automatically
+ctx.db.post().insert(Post { id: 0, title: "Hello".into() })?;
+
+// Does NOT trigger auto-increment: id will be 42
+ctx.db.post().insert(Post { id: 42, title: "World".into() })?;
+```
+
+This behavior allows you to migrate existing data with known IDs while still using auto-increment for new rows.
+
+## Sequences
+
+SpacetimeDB implements auto-increment using **sequences**, a mechanism loosely modeled after PostgreSQL sequences. A sequence is an internal counter that generates a series of integer values according to configurable parameters.
+
+### Sequence Parameters
+
+Each sequence has the following parameters:
+
+| Parameter | Description |
+|-----------|-------------|
+| `start` | The first value the sequence generates |
+| `min_value` | The minimum value in the sequence range |
+| `max_value` | The maximum value in the sequence range |
+| `increment` | The step between consecutive values (can be negative) |
+
+For auto-increment columns, SpacetimeDB creates a sequence with sensible defaults based on the column type. For example, a `u64` column gets a sequence starting at 1 with a maximum of 2^64 - 1.
+
+### Wrapping Behavior
+
+When a sequence reaches its maximum value, it wraps around to the minimum value and continues. For a sequence with `min_value = 1`, `max_value = 10`, and `increment = 1`, the values cycle as: 1, 2, 3, ..., 9, 10, 1, 2, 3, ...
+
+Sequences with negative increments wrap in the opposite direction. A sequence with `min_value = 1`, `max_value = 10`, and `increment = -1` starting at 5 produces: 5, 4, 3, 2, 1, 10, 9, 8, ...
+
+### Crash Recovery
+
+Sequences implement a crash recovery mechanism to ensure values are never reused after a database restart. Rather than persisting the current value after every increment, sequences allocate values in batches.
+
+When a sequence needs a new value and has exhausted its current allocation, it:
+
+1. Calculates the next batch of values
+2. Persists the allocation boundary to disk
+3. Returns values from the allocated range
+
+If the database crashes, it restarts from the persisted allocation boundary. This may skip some values that were allocated but never used, but guarantees that no value is ever assigned twice.
+
+For example, if a sequence allocates values in batches of 10:
+
+1. First insert triggers allocation of values 1-10
+2. Values 1, 2, 3 are used
+3. Database crashes
+4. On restart, the sequence resumes from value 1 (the allocation boundary)
+5. The sequence allocates values 1-10 again, but now starts fresh
+
+This design trades potential gaps in the sequence for durability and performance. The batch size balances the cost of persistence against the size of potential gaps.
+
+### Uniqueness Considerations
+
+Sequences generate values in a deterministic order, but wrapping means the same value can appear multiple times over the lifetime of a sequence. If your auto-increment column is also a primary key or has a unique constraint, inserting a duplicate value will fail.
+
+For most applications, the range of a 64-bit integer is large enough that wrapping never occurs in practice. However, if you use a smaller type like `u8` or `u16`, or if your application has very high insert volume, plan for the possibility of sequence exhaustion.
+
+### Concurrency and Gaps
+
+Sequences do not guarantee sequential ordering. Gaps can appear in auto-increment values for several reasons:
+
+1. **Crash recovery**: The batch allocation mechanism may skip values that were allocated but never used before a crash.
+
+2. **Concurrent transactions**: SpacetimeDB currently executes transactions serially, but reserves the right to execute them concurrently in future versions. With concurrent execution, two transactions inserting into the same table may receive interleaved sequence values.
+
+Even within a single reducer, you should not assume that consecutive inserts produce consecutive values. For example:
+
+```rust
+let a = ctx.db.post().insert(Post { id: 0, title: "First".into() })?;
+let b = ctx.db.post().insert(Post { id: 0, title: "Second".into() })?;
+// a.id might be 1 and b.id might be 3, not necessarily 1 and 2
+```
+
+If your application requires strictly sequential numbering without gaps, maintain that counter explicitly in a separate table rather than relying on auto-increment:
+
+```rust
+use spacetimedb::{ReducerContext, Table};
+
+#[derive(Clone)]
+#[spacetimedb::table(name = counter, public)]
+pub struct Counter {
+ #[primary_key]
+ name: String,
+ value: u64,
+}
+
+#[spacetimedb::table(name = invoice, public)]
+pub struct Invoice {
+ #[primary_key]
+ invoice_number: u64,
+ amount: u64,
+}
+
+#[spacetimedb::reducer]
+fn create_invoice(ctx: &ReducerContext, amount: u64) -> Result<(), String> {
+ // Get or create the counter
+ let mut counter = ctx.db.counter().name().find(&"invoice".to_string())
+ .unwrap_or(Counter { name: "invoice".to_string(), value: 0 });
+
+ // Increment and update
+ counter.value += 1;
+ ctx.db.counter().name().update(counter.clone());
+
+ // Use the counter value as the invoice number
+ ctx.db.invoice().insert(Invoice {
+ invoice_number: counter.value,
+ amount,
+ });
+
+ Ok(())
+}
+```
+
+This pattern guarantees sequential values because the counter update and row insert occur within the same transaction.
+
+## Combining with Other Attributes
+
+Auto-increment columns are commonly combined with primary keys:
+
+```rust
+#[spacetimedb::table(name = post, public)]
+pub struct Post {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ // ...
+}
+```
+
+Auto-increment columns can also be combined with unique constraints:
+
+```rust
+#[spacetimedb::table(name = item, public)]
+pub struct Item {
+ #[primary_key]
+ name: String,
+ #[unique]
+ #[auto_inc]
+ item_number: u32,
+}
+```
+
+Auto-increment **cannot** be combined with default values, since both attempt to populate the column automatically.
diff --git a/docs/docs/00200-core-concepts/00300-tables/00240-constraints.md b/docs/docs/00200-core-concepts/00300-tables/00240-constraints.md
new file mode 100644
index 00000000000..0a14605d1fd
--- /dev/null
+++ b/docs/docs/00200-core-concepts/00300-tables/00240-constraints.md
@@ -0,0 +1,306 @@
+---
+title: Constraints
+slug: /tables/constraints
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+Constraints enforce data integrity rules on your tables. SpacetimeDB supports primary key and unique constraints.
+
+## Primary Keys
+
+A primary key uniquely identifies each row in a table. It represents the identity of a row and determines how updates and deletes are handled.
+
+
+
+
+```typescript
+import { table, t } from 'spacetimedb/server';
+
+const user = table(
+ { name: 'user', public: true },
+ {
+ id: t.u64().primaryKey(),
+ name: t.string(),
+ email: t.string(),
+ }
+);
+```
+
+Use the `.primaryKey()` method on a column builder to mark it as the primary key.
+
+
+
+
+```csharp
+[SpacetimeDB.Table(Name = "User", Public = true)]
+public partial struct User
+{
+ [SpacetimeDB.PrimaryKey]
+ public ulong Id;
+ public string Name;
+ public string Email;
+}
+```
+
+Use the `[SpacetimeDB.PrimaryKey]` attribute to mark a field as the primary key.
+
+
+
+
+```rust
+#[spacetimedb::table(name = user, public)]
+pub struct User {
+ #[primary_key]
+ id: u64,
+ name: String,
+ email: String,
+}
+```
+
+Use the `#[primary_key]` attribute to mark a field as the primary key.
+
+
+
+
+### Primary Key Rules
+
+- **One per table**: A table can have at most one primary key column.
+- **Immutable identity**: The primary key defines the row's identity. Changing a primary key value is treated as deleting the old row and inserting a new one.
+- **Unique by definition**: Primary keys are automatically unique. No two rows can have the same primary key value.
+
+Because of the unique constraint, SpacetimeDB implements primary keys using a **unique index**. This index is created automatically.
+
+### Multi-Column Primary Keys
+
+SpacetimeDB does not yet support multi-column (composite) primary keys. If you need to look up rows by multiple columns, use a multi-column btree index combined with an auto-increment primary key:
+
+
+
+
+```typescript
+const inventory = table(
+ {
+ name: 'inventory',
+ public: true,
+ indexes: [
+ { name: 'by_user_item', algorithm: 'btree', columns: ['userId', 'itemId'] },
+ ],
+ },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ userId: t.u64(),
+ itemId: t.u64(),
+ quantity: t.u32(),
+ }
+);
+```
+
+
+
+
+```csharp
+[SpacetimeDB.Table(Name = "Inventory", Public = true)]
+[SpacetimeDB.Index.BTree(Name = "by_user_item", Columns = new[] { nameof(UserId), nameof(ItemId) })]
+public partial struct Inventory
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ public ulong UserId;
+ public ulong ItemId;
+ public uint Quantity;
+}
+```
+
+
+
+
+```rust
+#[spacetimedb::table(name = inventory, public, index(name = inventory_index, btree(columns = [user_id, item_id])))]
+pub struct Inventory {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ user_id: u64,
+ item_id: u64,
+ quantity: u32,
+}
+```
+
+
+
+
+This gives you efficient lookups by the column combination while using a simple auto-increment value as the primary key.
+
+### Updates and Primary Keys
+
+When you update a row, SpacetimeDB uses the primary key to determine whether it's a modification or a replacement:
+
+- **Same primary key**: The row is updated in place. Subscribers see an update event.
+- **Different primary key**: The old row is deleted and a new row is inserted. Subscribers see a delete event followed by an insert event.
+
+
+
+
+```typescript
+spacetimedb.reducer('update_user_name', { id: t.u64(), newName: t.string() }, (ctx, { id, newName }) => {
+ const user = ctx.db.user.id.find(id);
+ if (user) {
+ // This is an update — primary key (id) stays the same
+ ctx.db.user.id.update({ ...user, name: newName });
+ }
+});
+```
+
+
+
+
+```csharp
+[SpacetimeDB.Reducer]
+public static void UpdateUserName(ReducerContext ctx, ulong id, string newName)
+{
+ var user = ctx.Db.User.Id.Find(id);
+ if (user != null)
+ {
+ // This is an update — primary key (Id) stays the same
+ user.Name = newName;
+ ctx.Db.User.Id.Update(user);
+ }
+}
+```
+
+
+
+
+```rust
+#[spacetimedb::reducer]
+fn update_user_name(ctx: &ReducerContext, id: u64, new_name: String) -> Result<(), String> {
+ if let Some(mut user) = ctx.db.user().id().find(id) {
+ // This is an update — primary key (id) stays the same
+ user.name = new_name;
+ ctx.db.user().id().update(user);
+ }
+ Ok(())
+}
+```
+
+
+
+
+### Tables Without Primary Keys
+
+Tables don't require a primary key. Without one, the entire row acts as the primary key:
+
+- Rows are identified by their complete content
+- Updates require matching all fields
+- Duplicate rows are not possible. Inserting an identical row has no effect
+
+SpacetimeDB always maintains set semantics regardless of whether you define a primary key. The difference is what defines uniqueness: a primary key column, or the entire row.
+
+Primary keys add indexing overhead. If your table is only accessed by iterating over all rows (no lookups by key), omitting the primary key can improve performance.
+
+### Common Primary Key Patterns
+
+**Auto-incrementing IDs**: Combine `primaryKey()` with `autoInc()` for automatically assigned unique identifiers:
+
+```rust
+#[spacetimedb::table(name = post, public)]
+pub struct Post {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ title: String,
+ content: String,
+}
+```
+
+**Identity as primary key**: Use the caller's identity as the primary key for user-specific data:
+
+```rust
+#[spacetimedb::table(name = user_profile, public)]
+pub struct UserProfile {
+ #[primary_key]
+ identity: Identity,
+ display_name: String,
+ bio: String,
+}
+```
+
+This pattern ensures each identity can only have one profile and makes lookups by identity efficient.
+
+## Unique Columns
+
+Mark columns as unique to ensure no two rows can have the same value for that column.
+
+
+
+
+```typescript
+const user = table(
+ { name: 'user', public: true },
+ {
+ id: t.u32().primaryKey(),
+ email: t.string().unique(),
+ username: t.string().unique(),
+ }
+);
+```
+
+Use the `.unique()` method on a column builder.
+
+
+
+
+```csharp
+[SpacetimeDB.Table(Name = "User", Public = true)]
+public partial struct User
+{
+ [SpacetimeDB.PrimaryKey]
+ public uint Id;
+
+ [SpacetimeDB.Unique]
+ public string Email;
+
+ [SpacetimeDB.Unique]
+ public string Username;
+}
+```
+
+Use the `[SpacetimeDB.Unique]` attribute.
+
+
+
+
+```rust
+#[spacetimedb::table(name = user, public)]
+pub struct User {
+ #[primary_key]
+ id: u32,
+ #[unique]
+ email: String,
+ #[unique]
+ username: String,
+}
+```
+
+Use the `#[unique]` attribute.
+
+
+
+
+Unlike primary keys, you can have multiple unique columns on a single table. Unique columns also create an index that enables efficient lookups.
+
+## Primary Keys vs Unique Columns
+
+Both primary keys and unique columns enforce uniqueness, but they serve different purposes:
+
+| Aspect | Primary Key | Unique Column |
+|--------|-------------|---------------|
+| Purpose | Row identity | Data integrity |
+| Count per table | One | Multiple allowed |
+| Update behavior | Delete + Insert | In-place update |
+| Required | No | No |
diff --git a/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md b/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md
new file mode 100644
index 00000000000..db7024cbe24
--- /dev/null
+++ b/docs/docs/00200-core-concepts/00300-tables/00250-default-values.md
@@ -0,0 +1,128 @@
+---
+title: Default Values
+slug: /tables/default-values
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+Default values allow you to add new columns to existing tables during [automatic migrations](/databases/automatic-migrations). When you republish a module with a new column that has a default value, existing rows are automatically populated with that default.
+
+:::note
+New columns with default values must be added at the **end** of the table definition. Adding columns in the middle of a table is not supported.
+:::
+
+## Defining Default Values
+
+
+
+
+```typescript
+const player = table(
+ { name: 'player', public: true },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ name: t.string(),
+ // New columns added with defaults
+ score: t.u32().default(0),
+ isActive: t.bool().default(true),
+ bio: t.string().default(''),
+ }
+);
+```
+
+The `.default(value)` method can be chained on any column type builder. The value must match the column's type.
+
+
+
+
+```csharp
+[SpacetimeDB.Table(Name = "Player", Public = true)]
+public partial struct Player
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ public string Name;
+
+ // New columns added with defaults
+ [SpacetimeDB.Default(0u)]
+ public uint Score;
+
+ [SpacetimeDB.Default(true)]
+ public bool IsActive;
+
+ [SpacetimeDB.Default("")]
+ public string Bio;
+}
+```
+
+The `[SpacetimeDB.Default(value)]` attribute specifies the default value. The value is serialized to match the column's type.
+
+
+
+
+```rust
+#[spacetimedb::table(name = player, public)]
+pub struct Player {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ name: String,
+ // New columns added with defaults
+ #[default(0)]
+ score: u32,
+ #[default(true)]
+ is_active: bool,
+}
+```
+
+The `#[default(value)]` attribute specifies the default value. The expression must be const-evaluable (usable in a `const` context).
+
+:::note Rust Limitation
+Default values in Rust must be const-evaluable. This means you **cannot** use `String` defaults like `#[default("".to_string())]` because `.to_string()` is not a const fn. Only primitive types, enums, and other const-constructible types can have defaults.
+:::
+
+
+
+
+## Restrictions
+
+Default values **cannot** be combined with:
+- Primary keys
+- Unique constraints
+- [Auto-increment](/tables/auto-increment)
+
+This restriction exists because these attributes require the database to manage the column values, which conflicts with providing a static default.
+
+:::warning TypeScript: Unintuitive Error Message
+
+In TypeScript, this constraint is enforced at compile time. If you violate it, you'll see an **unintuitive error message**:
+
+```
+Expected 3 arguments, but got 2.
+```
+
+**This error means one of your columns has an invalid combination of `.default()` with `.primaryKey()`, `.unique()`, or `.autoInc()`.**
+
+For example, this code will produce the error:
+
+```typescript
+// ERROR: default() + primaryKey() is not allowed
+const badTable = table(
+ { name: 'bad' },
+ { id: t.u64().default(0n).primaryKey() } // <- Causes "Expected 3 arguments"
+);
+```
+
+**How to fix:** Remove either `.default()` or the constraint (`.primaryKey()`/`.unique()`/`.autoInc()`).
+
+:::
+
+## Use Cases
+
+- **Schema evolution**: Add new features to your application without losing existing data
+- **Optional fields**: Provide sensible defaults for fields that may not have been tracked historically
+- **Feature flags**: Add boolean columns with `default(false)` to enable new functionality gradually
diff --git a/docs/docs/00200-core-concepts/00300-tables/00300-indexes.md b/docs/docs/00200-core-concepts/00300-tables/00300-indexes.md
index da6d6762447..66d3c0fb692 100644
--- a/docs/docs/00200-core-concepts/00300-tables/00300-indexes.md
+++ b/docs/docs/00200-core-concepts/00300-tables/00300-indexes.md
@@ -7,10 +7,98 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-Indexes enable efficient querying of table data. SpacetimeDB supports B-Tree indexes on single or multiple columns.
+Indexes accelerate queries by maintaining sorted data structures alongside your tables. Without an index, finding rows that match a condition requires scanning every row. With an index, the database locates matching rows directly.
+
+## When to Use Indexes
+
+Add an index when you frequently query a column with equality or range conditions. Common scenarios include:
+
+- **Filtering by foreign key**: A `player_id` column in an inventory table benefits from an index when you query items belonging to a specific player.
+- **Range queries**: An `age` column benefits from an index when you query users within an age range.
+- **Sorting**: Columns used in ORDER BY clauses benefit from indexes that maintain sort order.
+
+Indexes consume additional memory and slow down inserts and updates, since the database must maintain the index structure. Add indexes based on your actual query patterns rather than speculatively.
+
+Primary keys and unique constraints automatically create indexes. You do not need to add a separate index for columns that already have these constraints.
+
+## Index Types
+
+SpacetimeDB supports two index types:
+
+| Type | Use Case | Key Types | Multi-Column |
+|------|----------|-----------|--------------|
+| B-tree | General purpose | Any | Yes |
+| Direct | Dense integer sequences | `u8`, `u16`, `u32`, `u64` | No |
+
+### B-tree Indexes
+
+B-trees maintain data in sorted order, enabling both equality lookups (`x = 5`) and range queries (`x > 5`, `x BETWEEN 1 AND 10`). The sorted structure also supports prefix matching on multi-column indexes. B-tree is the default and most commonly used index type.
+
+### Direct Indexes
+
+Direct indexes use array indexing instead of tree traversal, providing O(1) lookups for unsigned integer keys. SpacetimeDB uses the key value directly as an array offset, eliminating the need to search through a tree structure.
+
+Direct indexes perform well when:
+- Keys are dense (few gaps between values)
+- Keys start near zero
+- Insert patterns are sequential rather than random
+
+Direct indexes perform poorly when:
+- Keys are sparse (large gaps between values)
+- The first key inserted is a large number
+- Insert patterns are highly random
+
+Direct indexes only support single-column indexes on unsigned integer types. Use them for auto-increment primary keys or other dense sequential identifiers where you need maximum lookup performance.
+
+:::note
+Direct indexes are currently available in Rust and TypeScript. C# support is planned.
+:::
+
+
+
+
+```typescript
+const position = table(
+ { name: 'position', public: true },
+ {
+ id: t.u32().primaryKey().index('direct'),
+ x: t.f32(),
+ y: t.f32(),
+ z: t.f32(),
+ }
+);
+```
+
+
+
+
+```rust
+#[spacetimedb::table(name = position, public)]
+pub struct Position {
+ #[primary_key]
+ #[index(direct)]
+ id: u32,
+ x: f32,
+ y: f32,
+ z: f32,
+}
+```
+
+
+
+
+This example from the SpacetimeDB benchmarks uses direct indexes for a million entities with sequential IDs starting at 0, enabling O(1) lookups when joining position and velocity data by entity ID.
+
+For most use cases, B-tree indexes provide good performance without these restrictions. Consider direct indexes only when profiling reveals that index lookups are a bottleneck and your key distribution matches the ideal pattern.
## Single-Column Indexes
+A single-column index accelerates queries that filter on one column. You can define the index at the field level or the table level.
+
+### Field-Level Syntax
+
+The field-level syntax places the index declaration directly on the column:
+
@@ -20,20 +108,16 @@ const user = table(
{
id: t.u32().primaryKey(),
name: t.string().index('btree'),
+ age: t.u8().index('btree'),
}
);
-
-// Query using the index
-for (const user of ctx.db.user.name.filter('Alice')) {
- // users with name = 'Alice'
-}
```
```csharp
-[SpacetimeDB.Table(Public = true)]
+[SpacetimeDB.Table(Name = "User", Public = true)]
public partial struct User
{
[SpacetimeDB.PrimaryKey]
@@ -41,12 +125,9 @@ public partial struct User
[SpacetimeDB.Index.BTree]
public string Name;
-}
-// Query using the index
-foreach (var user in ctx.Db.User.Name.Filter("Alice"))
-{
- // users with Name == "Alice"
+ [SpacetimeDB.Index.BTree]
+ public byte Age;
}
```
@@ -60,11 +141,65 @@ pub struct User {
id: u32,
#[index(btree)]
name: String,
+ #[index(btree)]
+ age: u8,
}
+```
-// Query using the index
-for user in ctx.db.user().name().filter("Alice") {
- // users with name == "Alice"
+
+
+
+### Table-Level Syntax
+
+The table-level syntax defines indexes separately from columns. This approach allows you to name the index explicitly:
+
+
+
+
+```typescript
+const user = table(
+ {
+ name: 'user',
+ public: true,
+ indexes: [
+ { name: 'idx_age', algorithm: 'btree', columns: ['age'] },
+ ],
+ },
+ {
+ id: t.u32().primaryKey(),
+ name: t.string(),
+ age: t.u8(),
+ }
+);
+```
+
+
+
+
+```csharp
+[SpacetimeDB.Table(Name = "User", Public = true)]
+[SpacetimeDB.Index.BTree(Name = "idx_age", Columns = new[] { "Age" })]
+public partial struct User
+{
+ [SpacetimeDB.PrimaryKey]
+ public uint Id;
+
+ public string Name;
+
+ public byte Age;
+}
+```
+
+
+
+
+```rust
+#[spacetimedb::table(name = user, public, index(name = idx_age, btree(columns = [age])))]
+pub struct User {
+ #[primary_key]
+ id: u32,
+ name: String,
+ age: u8,
}
```
@@ -73,6 +208,20 @@ for user in ctx.db.user().name().filter("Alice") {
## Multi-Column Indexes
+A multi-column index (also called a composite index) spans multiple columns. The index maintains rows sorted by the first column, then by the second column within equal values of the first, and so on.
+
+Multi-column indexes support:
+- **Full match**: Queries that specify all indexed columns
+- **Prefix match**: Queries that specify the leftmost columns in order
+- **Range on trailing column**: A prefix of equality conditions followed by a range on the next column
+
+A multi-column index on `(player_id, level)` accelerates these queries:
+- `player_id = 123` (prefix match on first column)
+- `player_id = 123 AND level = 5` (full match)
+- `player_id = 123 AND level > 5` (prefix match with range)
+
+The same index does not accelerate a query on `level` alone, since `level` is not a prefix of the index.
+
@@ -82,11 +231,7 @@ const score = table(
name: 'score',
public: true,
indexes: [
- {
- name: 'byPlayerAndLevel',
- algorithm: 'btree',
- columns: ['player_id', 'level'],
- },
+ { name: 'by_player_and_level', algorithm: 'btree', columns: ['player_id', 'level'] },
],
},
{
@@ -95,36 +240,20 @@ const score = table(
points: t.i64(),
}
);
-
-// Query with prefix match
-for (const score of ctx.db.score.byPlayerAndLevel.filter(123)) {
- // scores with player_id = 123
-}
-
-// Query with range
-for (const score of ctx.db.score.byPlayerAndLevel.filter([123, [1, 10]])) {
- // player_id = 123, 1 <= level <= 10
-}
```
```csharp
-[SpacetimeDB.Table(Public = true)]
-[SpacetimeDB.Index.BTree(Name = "byPlayerAndLevel", Columns = new[] { "PlayerId", "Level" })]
+[SpacetimeDB.Table(Name = "Score", Public = true)]
+[SpacetimeDB.Index.BTree(Name = "by_player_and_level", Columns = new[] { "PlayerId", "Level" })]
public partial struct Score
{
public uint PlayerId;
public uint Level;
public long Points;
}
-
-// Query with prefix match
-foreach (var score in ctx.Db.Score.byPlayerAndLevel.Filter(123u))
-{
- // scores with PlayerId == 123
-}
```
@@ -137,12 +266,239 @@ pub struct Score {
level: u32,
points: i64,
}
+```
+
+
+
+
+## Querying with Indexes
+
+SpacetimeDB generates type-safe accessor methods for each index. These methods accept filter arguments and return matching rows.
+
+### Equality Queries
+
+Pass a single value to find rows where the indexed column equals that value:
+
+
+
+
+```typescript
+// Find users with a specific name
+for (const user of ctx.db.user.name.filter('Alice')) {
+ console.log(`Found user: ${user.id}`);
+}
+```
+
+
+
+
+```csharp
+// Find users with a specific name
+foreach (var user in ctx.Db.User.Name.Filter("Alice"))
+{
+ Log.Info($"Found user: {user.Id}");
+}
+```
+
+
+
-// Query with prefix match
-for score in ctx.db.score().by_player_and_level().filter(&123) {
- // scores with player_id == 123
+```rust
+// Find users with a specific name
+for user in ctx.db.user().name().filter("Alice") {
+ log::info!("Found user: {}", user.id);
}
```
+
+### Range Queries
+
+Pass a `Range` object to find rows where the indexed column falls within bounds. The `Range` constructor accepts `from` and `to` bounds, each specified as `{ tag: 'included', value }`, `{ tag: 'excluded', value }`, or `{ tag: 'unbounded' }`:
+
+
+
+
+```typescript
+import { Range } from 'spacetimedb/server';
+
+// Find users aged 18 to 65 (inclusive)
+for (const user of ctx.db.user.age.filter(
+ new Range({ tag: 'included', value: 18 }, { tag: 'included', value: 65 })
+)) {
+ console.log(`${user.name} is ${user.age}`);
+}
+
+// Find users aged 18 or older (from 18 inclusive, unbounded above)
+for (const user of ctx.db.user.age.filter(
+ new Range({ tag: 'included', value: 18 }, { tag: 'unbounded' })
+)) {
+ console.log(`${user.name} is an adult`);
+}
+
+// Find users younger than 18 (unbounded below, to 18 exclusive)
+for (const user of ctx.db.user.age.filter(
+ new Range({ tag: 'unbounded' }, { tag: 'excluded', value: 18 })
+)) {
+ console.log(`${user.name} is a minor`);
+}
+```
+
+
+
+
+```csharp
+// Find users aged 18 or older
+foreach (var user in ctx.Db.User.Age.Filter(new Bound.Inclusive(18), null))
+{
+ Log.Info($"{user.Name} is an adult");
+}
+```
+
+
+
+
+```rust
+// Find users aged 18 to 65 (inclusive)
+for user in ctx.db.user().age().filter(18..=65) {
+ log::info!("{} is {}", user.name, user.age);
+}
+
+// Find users aged 18 or older
+for user in ctx.db.user().age().filter(18..) {
+ log::info!("{} is an adult", user.name);
+}
+
+// Find users younger than 18
+for user in ctx.db.user().age().filter(..18) {
+ log::info!("{} is a minor", user.name);
+}
+```
+
+
+
+
+### Multi-Column Queries
+
+For multi-column indexes, pass a tuple of values. You can specify exact values for prefix columns and optionally a range for the trailing column:
+
+
+
+
+```typescript
+import { Range } from 'spacetimedb/server';
+
+// Find all scores for player 123 (prefix match on first column)
+for (const score of ctx.db.score.by_player_and_level.filter(123)) {
+ console.log(`Level ${score.level}: ${score.points} points`);
+}
+
+// Find scores for player 123 at levels 1-10 (inclusive)
+for (const score of ctx.db.score.by_player_and_level.filter([
+ 123,
+ new Range({ tag: 'included', value: 1 }, { tag: 'included', value: 10 })
+])) {
+ console.log(`Level ${score.level}: ${score.points} points`);
+}
+
+// Find the exact score for player 123 at level 5
+for (const score of ctx.db.score.by_player_and_level.filter([123, 5])) {
+ console.log(`Points: ${score.points}`);
+}
+```
+
+
+
+
+```csharp
+// Find all scores for player 123
+foreach (var score in ctx.Db.Score.by_player_and_level.Filter(123u))
+{
+ Log.Info($"Level {score.Level}: {score.Points} points");
+}
+```
+
+
+
+
+```rust
+// Find all scores for player 123 (prefix match)
+for score in ctx.db.score().by_player_and_level().filter(&123u32) {
+ log::info!("Level {}: {} points", score.level, score.points);
+}
+
+// Find scores for player 123 at levels 1-10
+for score in ctx.db.score().by_player_and_level().filter((123u32, 1u32..=10u32)) {
+ log::info!("Level {}: {} points", score.level, score.points);
+}
+
+// Find the exact score for player 123 at level 5
+for score in ctx.db.score().by_player_and_level().filter((123u32, 5u32)) {
+ log::info!("Points: {}", score.points);
+}
+```
+
+
+
+
+## Deleting with Indexes
+
+Indexes also accelerate deletions. Instead of scanning the entire table to find rows to delete, you can delete directly by index value:
+
+
+
+
+```typescript
+import { Range } from 'spacetimedb/server';
+
+// Delete all users named "Alice"
+const deleted = ctx.db.user.name.delete('Alice');
+console.log(`Deleted ${deleted} user(s)`);
+
+// Delete users younger than 18
+const deletedMinors = ctx.db.user.age.delete(
+ new Range({ tag: 'unbounded' }, { tag: 'excluded', value: 18 })
+);
+console.log(`Deleted ${deletedMinors} minor(s)`);
+```
+
+
+
+
+```csharp
+// Delete all users named "Alice"
+var deleted = ctx.Db.User.Name.Delete("Alice");
+Log.Info($"Deleted {deleted} user(s)");
+```
+
+
+
+
+```rust
+// Delete all users named "Alice"
+let deleted = ctx.db.user().name().delete("Alice");
+log::info!("Deleted {} user(s)", deleted);
+
+// Delete users in an age range
+let deleted = ctx.db.user().age().delete(..18);
+log::info!("Deleted {} minor(s)", deleted);
+```
+
+
+
+
+## Index Design Guidelines
+
+**Choose columns based on query patterns.** Index the columns that appear in your WHERE clauses and JOIN conditions. An unused index wastes memory.
+
+**Consider column order in multi-column indexes.** Place the most selective column (the one that narrows results most) first, followed by columns used in range conditions. An index on `(country, city)` works for queries on `country` alone or `country AND city`, but not for queries on `city` alone.
+
+**Avoid redundant indexes.** A multi-column index on `(a, b)` makes a separate index on `(a)` redundant, since the multi-column index handles prefix queries. However, an index on `(b)` is not redundant if you query `b` independently.
+
+**Balance read and write performance.** Each index speeds up reads but slows down writes. Tables with high write volume and few reads may benefit from fewer indexes.
+
+## Next Steps
+
+- Learn about [Constraints](/tables/constraints) for primary keys and unique indexes
+- See [Access Permissions](/tables/access-permissions) for querying tables from reducers
diff --git a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md
index 7c9025a6f95..d8111517587 100644
--- a/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md
+++ b/docs/docs/00200-core-concepts/00300-tables/00400-access-permissions.md
@@ -7,11 +7,101 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-SpacetimeDB enforces different levels of table access depending on the context. All contexts access tables through `ctx.db`, but the available operations differ based on whether the context is read-write or read-only.
+SpacetimeDB controls data access through table visibility and context-based permissions. Tables can be public or private, and different execution contexts (reducers, views, clients) have different levels of access.
+
+## Public and Private Tables
+
+Tables are **private** by default. Private tables can only be accessed by reducers and views running on the server. Clients cannot query, subscribe to, or see private tables.
+
+**Public** tables are exposed to clients for read access through subscriptions and queries. Clients can see public table data but can only modify it by calling reducers.
+
+
+
+
+```typescript
+// Private table (default) - only accessible from server-side code
+const internalConfig = table(
+ { name: 'internal_config' },
+ {
+ key: t.string().primaryKey(),
+ value: t.string(),
+ }
+);
+
+// Public table - clients can subscribe and query
+const player = table(
+ { name: 'player', public: true },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ name: t.string(),
+ score: t.u64(),
+ }
+);
+```
+
+
+
+
+```csharp
+// Private table (default) - only accessible from server-side code
+[SpacetimeDB.Table(Name = "InternalConfig")]
+public partial struct InternalConfig
+{
+ [SpacetimeDB.PrimaryKey]
+ public string Key;
+ public string Value;
+}
+
+// Public table - clients can subscribe and query
+[SpacetimeDB.Table(Name = "Player", Public = true)]
+public partial struct Player
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ public string Name;
+ public ulong Score;
+}
+```
+
+
+
+
+```rust
+// Private table (default) - only accessible from server-side code
+#[spacetimedb::table(name = internal_config)]
+pub struct InternalConfig {
+ #[primary_key]
+ key: String,
+ value: String,
+}
+
+// Public table - clients can subscribe and query
+#[spacetimedb::table(name = player, public)]
+pub struct Player {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ name: String,
+ score: u64,
+}
+```
+
+
+
+
+Use private tables for:
+- Internal configuration or state that clients should not see
+- Sensitive data like password hashes or API keys
+- Intermediate computation results
+
+Use public tables for:
+- Data that clients need to display or interact with
+- Game state, user profiles, or other user-facing data
## Reducers - Read-Write Access
-Reducers receive a `ReducerContext` which provides full read-write access to tables. They can perform all CRUD operations: insert, read, update, and delete.
+Reducers receive a `ReducerContext` which provides full read-write access to all tables (both public and private). They can perform all CRUD operations: insert, read, update, and delete.
@@ -157,7 +247,7 @@ See the [Procedures documentation](/functions/procedures) for more details on us
## Views - Read-Only Access
-Views receive a `ViewContext` or `AnonymousViewContext` which provides read-only access to tables. They can query and iterate tables, but cannot insert, update, or delete rows.
+[Views](/functions/views) receive a `ViewContext` or `AnonymousViewContext` which provides read-only access to all tables (both public and private). They can query and iterate tables, but cannot insert, update, or delete rows.
@@ -184,9 +274,9 @@ public static List FindUsersByName(ViewContext ctx)
{
// Can read and filter
return ctx.Db.User.Name.Filter("Alice").ToList();
-
+
// Cannot insert, update, or delete
- // ctx.Db.user.Insert(...) // ❌ Method not available
+ // ctx.Db.User.Insert(...) // ❌ Method not available
}
```
@@ -207,6 +297,436 @@ fn find_users_by_name(ctx: &ViewContext) -> Vec {
+See the [Views documentation](/functions/views) for more details on defining and querying views.
+
+## Using Views for Fine-Grained Access Control
+
+While table visibility controls whether clients can access a table at all, views provide fine-grained control over which rows and columns clients can see. Views can read from private tables and expose only the data appropriate for each client.
+
+:::note
+Views can only access table data through indexed lookups, not by scanning all rows. This restriction ensures views remain performant. See the [Views documentation](/functions/views) for details.
+:::
+
+### Filtering Rows by Caller
+
+Use views with `ViewContext` to return only the rows that belong to the caller. The view accesses the caller's identity through `ctx.sender` and uses it to look up rows via an index.
+
+
+
+
+```typescript
+import { table, t, schema } from 'spacetimedb/server';
+
+// Private table containing all messages
+const message = table(
+ { name: 'message' }, // Private by default
+ {
+ id: t.u64().primaryKey().autoInc(),
+ sender: t.identity().index('btree'),
+ recipient: t.identity().index('btree'),
+ content: t.string(),
+ timestamp: t.timestamp(),
+ }
+);
+
+const spacetimedb = schema(message);
+
+// Public view that only returns messages the caller can see
+spacetimedb.view(
+ { name: 'my_messages', public: true },
+ t.array(message.rowType),
+ (ctx) => {
+ // Look up messages by index where caller is sender or recipient
+ const sent = Array.from(ctx.db.message.sender.filter(ctx.sender));
+ const received = Array.from(ctx.db.message.recipient.filter(ctx.sender));
+ return [...sent, ...received];
+ }
+);
+```
+
+
+
+
+```csharp
+using SpacetimeDB;
+
+public partial class Module
+{
+ // Private table containing all messages
+ [SpacetimeDB.Table(Name = "Message")] // Private by default
+ public partial struct Message
+ {
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ [SpacetimeDB.Index.BTree]
+ public Identity Sender;
+ [SpacetimeDB.Index.BTree]
+ public Identity Recipient;
+ public string Content;
+ public Timestamp Timestamp;
+ }
+
+ // Public view that only returns messages the caller can see
+ [SpacetimeDB.View(Name = "MyMessages", Public = true)]
+ public static List MyMessages(ViewContext ctx)
+ {
+ // Look up messages by index where caller is sender or recipient
+ var sent = ctx.Db.Message.Sender.Filter(ctx.Sender).ToList();
+ var received = ctx.Db.Message.Recipient.Filter(ctx.Sender).ToList();
+ sent.AddRange(received);
+ return sent;
+ }
+
+
+```
+
+
+
+
+```rust
+use spacetimedb::{Identity, Timestamp, ViewContext};
+
+// Private table containing all messages
+#[spacetimedb::table(name = message)] // Private by default
+pub struct Message {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ #[index(btree)]
+ sender: Identity,
+ #[index(btree)]
+ recipient: Identity,
+ content: String,
+ timestamp: Timestamp,
+}
+
+// Public view that only returns messages the caller can see
+#[spacetimedb::view(name = my_messages, public)]
+fn my_messages(ctx: &ViewContext) -> Vec {
+ // Look up messages by index where caller is sender or recipient
+ let sent: Vec<_> = ctx.db.message().sender().filter(&ctx.sender).collect();
+ let received: Vec<_> = ctx.db.message().recipient().filter(&ctx.sender).collect();
+ sent.into_iter().chain(received).collect()
+}
+```
+
+
+
+
+Clients querying `my_messages` will only see their own messages, even though all messages are stored in the same table.
+
+### Hiding Sensitive Columns
+
+Use views to return a custom type that omits sensitive columns. The view reads from a table with sensitive data and returns a projection containing only the columns clients should see.
+
+
+
+
+```typescript
+import {schema, t, table} from 'spacetimedb/server';
+
+// Private table with sensitive data
+const userAccount = table(
+ { name: 'user_account' }, // Private by default
+ {
+ id: t.u64().primaryKey().autoInc(),
+ identity: t.identity().unique(),
+ username: t.string(),
+ email: t.string(),
+ passwordHash: t.string(), // Sensitive
+ apiKey: t.string(), // Sensitive
+ createdAt: t.timestamp(),
+ }
+);
+
+const spacetimedb = schema(userAccount);
+
+// Public type without sensitive columns
+const publicUserProfile = t.row('PublicUserProfile', {
+ id: t.u64(),
+ username: t.string(),
+ createdAt: t.timestamp(),
+});
+
+// Public view that returns the caller's profile without sensitive data
+spacetimedb.view(
+ { name: 'my_profile', public: true },
+ t.option(publicUserProfile),
+ (ctx) => {
+ // Look up the caller's account by their identity (unique index)
+ const user = ctx.db.userAccount.identity.find(ctx.sender);
+ if (!user) return null;
+ return {
+ id: user.id,
+ username: user.username,
+ createdAt: user.createdAt,
+ // email, passwordHash, and apiKey are not included
+ };
+ }
+);
+```
+
+
+
+
+```csharp
+using SpacetimeDB;
+
+public partial class Module
+{
+ // Private table with sensitive data
+ [SpacetimeDB.Table(Name = "UserAccount")] // Private by default
+ public partial struct UserAccount
+ {
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+ [SpacetimeDB.Unique]
+ public Identity Identity;
+ public string Username;
+ public string Email;
+ public string PasswordHash; // Sensitive
+ public string ApiKey; // Sensitive
+ public Timestamp CreatedAt;
+ }
+
+ // Public type without sensitive columns
+ [SpacetimeDB.Type]
+ public partial struct PublicUserProfile
+ {
+ public ulong Id;
+ public string Username;
+ public Timestamp CreatedAt;
+ }
+
+ // Public view that returns the caller's profile without sensitive data
+ [SpacetimeDB.View(Name = "MyProfile", Public = true)]
+ public static PublicUserProfile? MyProfile(ViewContext ctx)
+ {
+ // Look up the caller's account by their identity (unique index)
+ if (ctx.Db.UserAccount.Identity.Find(ctx.Sender) is not UserAccount user)
+ {
+ return null;
+ }
+ return new PublicUserProfile
+ {
+ Id = user.Id,
+ Username = user.Username,
+ CreatedAt = user.CreatedAt,
+ // Email, PasswordHash, and ApiKey are not included
+ };
+ }
+}
+```
+
+
+
+
+```rust
+use spacetimedb::{SpacetimeType, ViewContext, Timestamp, Identity};
+
+// Private table with sensitive data
+#[spacetimedb::table(name = user_account)] // Private by default
+pub struct UserAccount {
+ #[primary_key]
+ #[auto_inc]
+ id: u64,
+ #[unique]
+ identity: Identity,
+ username: String,
+ email: String,
+ password_hash: String, // Sensitive
+ api_key: String, // Sensitive
+ created_at: Timestamp,
+}
+
+// Public type without sensitive columns
+#[derive(SpacetimeType)]
+pub struct PublicUserProfile {
+ id: u64,
+ username: String,
+ created_at: Timestamp,
+}
+
+// Public view that returns the caller's profile without sensitive data
+#[spacetimedb::view(name = my_profile, public)]
+fn my_profile(ctx: &ViewContext) -> Option {
+ // Look up the caller's account by their identity (unique index)
+ let user = ctx.db.user_account().identity().find(&ctx.sender)?;
+ Some(PublicUserProfile {
+ id: user.id,
+ username: user.username,
+ created_at: user.created_at,
+ // email, password_hash, and api_key are not included
+ })
+}
+```
+
+
+
+
+Clients can query `my_profile` to see their username and creation date, but never see their email address, password hash, or API key.
+
+### Combining Both Techniques
+
+Views can combine row filtering and column projection. This example returns team members who report to the caller, with salary information hidden:
+
+
+
+
+```typescript
+import { table, t, schema } from 'spacetimedb/server';
+
+// Private table with all employee data
+const employee = table(
+ { name: 'employee' },
+ {
+ id: t.u64().primaryKey(),
+ identity: t.identity().unique(),
+ name: t.string(),
+ department: t.string(),
+ salary: t.u64(), // Sensitive
+ managerId: t.option(t.u64()).index('btree'),
+ }
+);
+
+const spacetimedb = schema(employee);
+
+// Public type for team members (no salary)
+const teamMember = t.row('TeamMember', {
+ id: t.u64(),
+ name: t.string(),
+ department: t.string(),
+});
+
+// View that returns only the caller's team members, without salary info
+spacetimedb.view(
+ { name: 'my_team', public: true },
+ t.array(teamMember),
+ (ctx) => {
+ // Find the caller's employee record by identity (unique index)
+ const me = ctx.db.employee.identity.find(ctx.sender);
+ if (!me) return [];
+
+ // Look up employees who report to the caller by managerId index
+ return Array.from(ctx.db.employee.managerId.filter(me.id)).map(emp => ({
+ id: emp.id,
+ name: emp.name,
+ department: emp.department,
+ // salary is not included
+ }));
+ }
+);
+```
+
+
+
+
+```csharp
+using SpacetimeDB;
+
+public partial class Module
+{
+ // Private table with all employee data
+ [SpacetimeDB.Table(Name = "Employee")]
+ public partial struct Employee
+ {
+ [SpacetimeDB.PrimaryKey]
+ public ulong Id;
+ [SpacetimeDB.Unique]
+ public Identity Identity;
+ public string Name;
+ public string Department;
+ public ulong Salary; // Sensitive
+ [SpacetimeDB.Index.BTree]
+ public ulong? ManagerId;
+ }
+
+ // Public type for team members (no salary)
+ [SpacetimeDB.Type]
+ public partial struct TeamMember
+ {
+ public ulong Id;
+ public string Name;
+ public string Department;
+ }
+
+ // View that returns only the caller's team members, without salary info
+ [SpacetimeDB.View(Name = "MyTeam", Public = true)]
+ public static List MyTeam(ViewContext ctx)
+ {
+ // Find the caller's employee record by identity (unique index)
+ if (ctx.Db.Employee.Identity.Find(ctx.Sender) is not Employee me)
+ {
+ return new List();
+ }
+
+ // Look up employees who report to the caller by ManagerId index
+ return ctx.Db.Employee.ManagerId.Filter(me.Id)
+ .Select(emp => new TeamMember
+ {
+ Id = emp.Id,
+ Name = emp.Name,
+ Department = emp.Department,
+ // Salary is not included
+ })
+ .ToList();
+ }
+}
+```
+
+
+
+
+```rust
+use spacetimedb::{SpacetimeType, Identity, ViewContext};
+
+// Private table with all employee data
+#[spacetimedb::table(name = employee)]
+pub struct Employee {
+ #[primary_key]
+ id: u64,
+ #[unique]
+ identity: Identity,
+ name: String,
+ department: String,
+ salary: u64, // Sensitive
+ #[index(btree)]
+ manager_id: Option,
+}
+
+// Public type for team members (no salary)
+#[derive(SpacetimeType)]
+pub struct TeamMember {
+ id: u64,
+ name: String,
+ department: String,
+}
+
+// View that returns only the caller's team members, without salary info
+#[spacetimedb::view(name = my_team, public)]
+fn my_team(ctx: &ViewContext) -> Vec {
+ // Find the caller's employee record by identity (unique index)
+ let Some(me) = ctx.db.employee().identity().find(&ctx.sender) else {
+ return vec![];
+ };
+
+ // Look up employees who report to the caller by manager_id index
+ ctx.db.employee().manager_id().filter(&Some(me.id))
+ .map(|emp| TeamMember {
+ id: emp.id,
+ name: emp.name,
+ department: emp.department,
+ // salary is not included
+ })
+ .collect()
+}
+```
+
+
+
+
## Client Access - Read-Only Access
-Clients connect to databases and can access public tables through subscriptions and queries. See the [Subscriptions documentation](/subscriptions) for details on client-side table access.
+Clients connect to databases and can access public tables and views through subscriptions and queries. They cannot access private tables directly. See the [Subscriptions documentation](/subscriptions) for details on client-side table access.
diff --git a/docs/docs/00200-core-concepts/00300-tables/00500-scheduled-tables.md b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md
similarity index 89%
rename from docs/docs/00200-core-concepts/00300-tables/00500-scheduled-tables.md
rename to docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md
index c1663e3efd2..0c3c816e34d 100644
--- a/docs/docs/00200-core-concepts/00300-tables/00500-scheduled-tables.md
+++ b/docs/docs/00200-core-concepts/00300-tables/00500-schedule-tables.md
@@ -1,6 +1,6 @@
---
-title: Scheduled Tables
-slug: /tables/scheduled-tables
+title: Schedule Tables
+slug: /tables/schedule-tables
---
import Tabs from '@theme/Tabs';
@@ -9,7 +9,11 @@ import TabItem from '@theme/TabItem';
Tables can trigger [reducers](/functions/reducers) or [procedures](/functions/procedures) at specific times by including a special scheduling column. This allows you to schedule future actions like sending reminders, expiring items, or running periodic maintenance tasks.
-## Defining a Scheduled Table
+## Defining a Schedule Table
+
+:::note Why "scheduled" in the code?
+The table attribute uses `scheduled` (with a "d") because it refers to the **scheduled reducer** - the function that will be scheduled for execution. The table itself is a "schedule table" that stores schedules, while the reducer it triggers is a "scheduled reducer".
+:::
@@ -79,7 +83,7 @@ fn send_reminder(ctx: &ReducerContext, reminder: Reminder) -> Result<(), String>
## How It Works
1. **Insert a row** with a `schedule_at` time
-2. **SpacetimeDB monitors** the scheduled table
+2. **SpacetimeDB monitors** the schedule table
3. **When the time arrives**, the specified reducer/procedure is automatically called with the row as a parameter
4. **The row is typically deleted** or updated by the reducer after processing
diff --git a/docs/docs/00200-core-concepts/00300-tables/00600-performance.md b/docs/docs/00200-core-concepts/00300-tables/00600-performance.md
index eb71d11545b..f132903069e 100644
--- a/docs/docs/00200-core-concepts/00300-tables/00600-performance.md
+++ b/docs/docs/00200-core-concepts/00300-tables/00600-performance.md
@@ -419,7 +419,7 @@ public static void SpawnEnemies(ReducerContext ctx, uint count)
{
for (uint i = 0; i < count; i++)
{
- ctx.Db.enemy.Insert(new Enemy
+ ctx.Db.Enemy.Insert(new Enemy
{
Id = 0, // auto_inc
Health = 100
@@ -493,7 +493,7 @@ Be mindful of unbounded table growth:
- Implement cleanup reducers for temporary data
- Archive or delete old records
-- Use scheduled tables to automatically expire data
+- Use schedule tables to automatically expire data
- Consider pagination for large result sets
## Next Steps
diff --git a/docs/docs/00200-core-concepts/00400-subscriptions.md b/docs/docs/00200-core-concepts/00400-subscriptions.md
index 2a4312fe090..d77eea7de1a 100644
--- a/docs/docs/00200-core-concepts/00400-subscriptions.md
+++ b/docs/docs/00200-core-concepts/00400-subscriptions.md
@@ -18,7 +18,30 @@ By using these interfaces, you can create efficient and responsive client applic
## SubscriptionBuilder
-
+
+
+
+```typescript
+interface SubscriptionBuilder {
+ // Register a callback to run when the subscription is applied.
+ onApplied(callback: (ctx: SubscriptionEventContext) => void): SubscriptionBuilder;
+
+ // Register a callback to run when the subscription fails.
+ // This callback may run when attempting to apply the subscription,
+ // or later during the subscription's lifetime if the module's interface changes.
+ onError(callback: (ctx: ErrorContext, error: Error) => void): SubscriptionBuilder;
+
+ // Subscribe to the following SQL queries.
+ // Returns immediately; callbacks are invoked when data arrives from the server.
+ subscribe(querySqls: string[]): SubscriptionHandle;
+
+ // Subscribe to all rows from all tables.
+ // Intended for applications where memory and bandwidth are not concerns.
+ subscribeToAllTables(): void;
+}
+```
+
+
```cs
@@ -119,7 +142,27 @@ A client can react to these updates by registering row callbacks for the appropr
### Example Usage
-
+
+
+
+```typescript
+// Establish a database connection
+import { DbConnection } from './module_bindings';
+
+const conn = DbConnection.builder()
+ .withUri('https://maincloud.spacetimedb.com')
+ .withModuleName('my_module')
+ .build();
+
+// Register a subscription with the database
+const userSubscription = conn
+ .subscriptionBuilder()
+ .onApplied((ctx) => { /* handle applied state */ })
+ .onError((ctx, error) => { /* handle error */ })
+ .subscribe(['SELECT * FROM user', 'SELECT * FROM message']);
+```
+
+
```cs
@@ -154,7 +197,27 @@ let subscription_handle = conn
## SubscriptionHandle
-
+
+
+
+```typescript
+interface SubscriptionHandle {
+ // Whether the subscription has ended (unsubscribed or terminated due to error).
+ isEnded(): boolean;
+
+ // Whether the subscription is currently active.
+ isActive(): boolean;
+
+ // Unsubscribe from the query controlled by this handle.
+ // Throws if called more than once.
+ unsubscribe(): void;
+
+ // Unsubscribe and call onEnded when rows are removed from the client cache.
+ unsubscribeThen(onEnded?: (ctx: SubscriptionEventContext) => void): void;
+}
+```
+
+
```cs
@@ -226,7 +289,49 @@ clients can dynamically subscribe to different subsets of the database as their
### Example Usage
-
+
+
+
+Consider a game client that displays shop items and discounts based on a player's level.
+You subscribe to `shop_items` and `shop_discounts` when a player is at level 5:
+
+```typescript
+const conn = DbConnection.builder()
+ .withUri('https://maincloud.spacetimedb.com')
+ .withModuleName('my_module')
+ .build();
+
+const shopItemsSubscription = conn
+ .subscriptionBuilder()
+ .onApplied((ctx) => { /* handle applied state */ })
+ .onError((ctx, error) => { /* handle error */ })
+ .subscribe([
+ 'SELECT * FROM shop_items WHERE required_level <= 5',
+ 'SELECT * FROM shop_discounts WHERE required_level <= 5',
+ ]);
+```
+
+Later, when the player reaches level 6 and new items become available,
+you can subscribe to the new queries and unsubscribe from the old ones:
+
+```typescript
+const newShopItemsSubscription = conn
+ .subscriptionBuilder()
+ .onApplied((ctx) => { /* handle applied state */ })
+ .onError((ctx, error) => { /* handle error */ })
+ .subscribe([
+ 'SELECT * FROM shop_items WHERE required_level <= 6',
+ 'SELECT * FROM shop_discounts WHERE required_level <= 6',
+ ]);
+
+if (shopItemsSubscription.isActive()) {
+ shopItemsSubscription.unsubscribe();
+}
+```
+
+All other subscriptions continue to remain in effect.
+
+
Consider a game client that displays shop items and discounts based on a player's level.
@@ -328,7 +433,34 @@ This will improve throughput by reducing the amount of data transferred from the
#### Example
-
+
+
+
+```typescript
+const conn = DbConnection.builder()
+ .withUri('https://maincloud.spacetimedb.com')
+ .withModuleName('my_module')
+ .build();
+
+// Never need to unsubscribe from global subscriptions
+const globalSubscriptions = conn
+ .subscriptionBuilder()
+ .subscribe([
+ // Global messages the client should always display
+ 'SELECT * FROM announcements',
+ // A description of rewards for in-game achievements
+ 'SELECT * FROM badges',
+ ]);
+
+// May unsubscribe to shop_items as player advances
+const shopSubscription = conn
+ .subscriptionBuilder()
+ .subscribe([
+ 'SELECT * FROM shop_items WHERE required_level <= 5',
+ ]);
+```
+
+
```cs
@@ -391,7 +523,40 @@ unsubscribing from it does not result in any server processing or data serializt
#### Example
-
+
+
+
+```typescript
+const conn = DbConnection.builder()
+ .withUri('https://maincloud.spacetimedb.com')
+ .withModuleName('my_module')
+ .build();
+
+// Initial subscription: player at level 5.
+const shopSubscription = conn
+ .subscriptionBuilder()
+ .subscribe([
+ // For displaying the price of shop items in the player's currency of choice
+ 'SELECT * FROM exchange_rates',
+ 'SELECT * FROM shop_items WHERE required_level <= 5',
+ ]);
+
+// New subscription: player now at level 6, which overlaps with the previous query.
+const newShopSubscription = conn
+ .subscriptionBuilder()
+ .subscribe([
+ // For displaying the price of shop items in the player's currency of choice
+ 'SELECT * FROM exchange_rates',
+ 'SELECT * FROM shop_items WHERE required_level <= 6',
+ ]);
+
+// Unsubscribe from the old subscription once the new one is in place.
+if (shopSubscription.isActive()) {
+ shopSubscription.unsubscribe();
+}
+```
+
+
```cs
diff --git a/docs/docs/00200-core-concepts/00600-client-sdk-languages/00300-connection.md b/docs/docs/00200-core-concepts/00600-client-sdk-languages/00300-connection.md
index bccd43d1f38..12639ab748d 100644
--- a/docs/docs/00200-core-concepts/00600-client-sdk-languages/00300-connection.md
+++ b/docs/docs/00200-core-concepts/00600-client-sdk-languages/00300-connection.md
@@ -28,7 +28,7 @@ Create a connection using the `DbConnection` builder pattern:
import { DbConnection } from './module_bindings';
const conn = new DbConnection.builder()
- .withUri("http://localhost:3000")
+ .withUri("https://maincloud.spacetimedb.com")
.withModuleName("my_database");
```
@@ -39,7 +39,7 @@ const conn = new DbConnection.builder()
using SpacetimeDB;
var conn = DbConnection.Builder()
- .WithUri(new Uri("http://localhost:3000"))
+ .WithUri(new Uri("https://maincloud.spacetimedb.com"))
.WithModuleName("my_database")
.Build();
```
@@ -51,7 +51,7 @@ var conn = DbConnection.Builder()
use module_bindings::DbConnection;
let conn = DbConnection::builder()
- .with_uri("http://localhost:3000")
+ .with_uri("https://maincloud.spacetimedb.com")
.with_module_name("my_database")
.build();
```
@@ -63,7 +63,7 @@ let conn = DbConnection::builder()
#include "ModuleBindings/DbConnection.h"
UDbConnection* Conn = UDbConnection::Builder()
- ->WithUri(TEXT("http://localhost:3000"))
+ ->WithUri(TEXT("https://maincloud.spacetimedb.com"))
->WithModuleName(TEXT("my_database"))
->Build();
```
@@ -71,7 +71,7 @@ UDbConnection* Conn = UDbConnection::Builder()
-Replace `"http://localhost:3000"` with your SpacetimeDB host URI, and `"my_database"` with your database's name or identity.
+Replace `"https://maincloud.spacetimedb.com"` with your SpacetimeDB host URI, and `"my_database"` with your database's name or identity.
### Connecting to MainCloud
@@ -233,7 +233,7 @@ Register callbacks to observe connection state changes:
```typescript
const conn = DbConnection.builder()
- .withUri("http://localhost:3000")
+ .withUri("https://maincloud.spacetimedb.com")
.withModuleName("my_database")
.onConnect((conn, identity, token) => {
console.log(`Connected! Identity: ${identity.toHexString()}`);
@@ -253,7 +253,7 @@ const conn = DbConnection.builder()
```csharp
var conn = DbConnection.Builder()
- .WithUri(new Uri("http://localhost:3000"))
+ .WithUri(new Uri("https://maincloud.spacetimedb.com"))
.WithModuleName("my_database")
.OnConnect((conn, identity, token) =>
{
@@ -283,7 +283,7 @@ var conn = DbConnection.Builder()
```rust
let conn = DbConnection::builder()
- .with_uri("http://localhost:3000")
+ .with_uri("https://maincloud.spacetimedb.com")
.with_module_name("my_database")
.on_connect(|_ctx, _identity, token| {
println!("Connected! Saving token...");
@@ -319,7 +319,7 @@ DisconnectDelegate.BindDynamic(this, &AMyActor::OnDisconnected);
// Build connection with callbacks
UDbConnection* Conn = UDbConnection::Builder()
- ->WithUri(TEXT("http://localhost:3000"))
+ ->WithUri(TEXT("https://maincloud.spacetimedb.com"))
->WithModuleName(TEXT("my_database"))
->OnConnect(ConnectDelegate)
->OnConnectError(ErrorDelegate)
diff --git a/docs/docs/00200-core-concepts/00600-client-sdk-languages/00600-csharp-reference.md b/docs/docs/00200-core-concepts/00600-client-sdk-languages/00600-csharp-reference.md
index 4e92caec3b8..75a20d336cf 100644
--- a/docs/docs/00200-core-concepts/00600-client-sdk-languages/00600-csharp-reference.md
+++ b/docs/docs/00200-core-concepts/00600-client-sdk-languages/00600-csharp-reference.md
@@ -946,9 +946,9 @@ See the [module docs](/intro/key-architecture#connectionid) for more details.
### Type `Timestamp`
A point in time, measured in microseconds since the Unix epoch.
-See the [module docs](/docs/tables/columns) for more details.
+See the [module docs](/docs/tables/column-types) for more details.
### Type `TaggedEnum`
A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) type.
-See the [module docs](/docs/tables/columns) for more details.
+See the [module docs](/docs/tables/column-types) for more details.
diff --git a/docs/docs/00300-resources/00100-how-to/00400-row-level-security.md b/docs/docs/00300-resources/00100-how-to/00400-row-level-security.md
index 63c20f89e04..eff693471ec 100644
--- a/docs/docs/00300-resources/00100-how-to/00400-row-level-security.md
+++ b/docs/docs/00300-resources/00100-how-to/00400-row-level-security.md
@@ -7,8 +7,18 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-:::warning Consider Using Views Instead
-Before implementing Row Level Security, consider using [Views](/functions/views) for access control. Views provide a simpler, more flexible approach to controlling data visibility without the complexity and performance overhead of RLS. Views are first-class functions that allow you to define custom, subscribable queries with full control over what data clients can access. RLS is experimental and should only be used for advanced use cases where views are insufficient.
+:::danger Experimental Feature - Use Views Instead
+**Row Level Security is an experimental, unstable feature.** The API may change or be removed in future releases.
+
+For access control, **use [Views](/functions/views) instead**. Views provide:
+- A simpler, more flexible approach to controlling data visibility
+- Better performance characteristics
+- Full control over which rows and columns clients can access
+- The ability to filter by caller identity using `ViewContext`
+
+See [Using Views for Fine-Grained Access Control](/tables/access-permissions#using-views-for-fine-grained-access-control) for examples of implementing row and column filtering with views.
+
+Only use RLS if you have a specific use case that views cannot address.
:::
Row Level Security (RLS) allows module authors to restrict which rows of a public table each client can access.
@@ -16,7 +26,7 @@ These access rules are expressed in SQL and evaluated automatically for queries
## Enabling RLS
-RLS is currently **experimental** and must be explicitly enabled in your module.
+RLS is **experimental and unstable**. It must be explicitly enabled in your module, and the API may change in future releases.
diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts
index 58eac0d8cd5..fc6731af68d 100644
--- a/docs/docusaurus.config.ts
+++ b/docs/docusaurus.config.ts
@@ -160,11 +160,6 @@ const config: Config = {
label: 'Pricing',
position: 'right',
},
- {
- href: 'https://spacetimedb.com/maincloud',
- label: 'Maincloud',
- position: 'right',
- },
{
href: 'https://spacetimedb.com/blog',
label: 'Blog',
@@ -175,6 +170,11 @@ const config: Config = {
label: 'Community',
position: 'right',
},
+ {
+ href: 'https://spacetimedb.com/spacerace',
+ label: 'Spacerace',
+ position: 'right',
+ },
{
href: 'https://spacetimedb.com/login',
label: 'Login',
diff --git a/docs/llms/docs-benchmark-analysis.md b/docs/llms/docs-benchmark-analysis.md
new file mode 100644
index 00000000000..ee0e02bf837
--- /dev/null
+++ b/docs/llms/docs-benchmark-analysis.md
@@ -0,0 +1,80 @@
+# Benchmark Failure Analysis
+
+Generated from: `C:\Users\Tyler\Developer\SpacetimeDB\tools\xtask-llm-benchmark\../../docs/llms/docs-benchmark-details.json`
+
+## Summary
+
+- **Total failures analyzed**: 26
+
+## Analysis
+
+# SpacetimeDB Benchmark Test Failures Analysis
+
+## Rust Failures
+
+### 1. Root Causes
+- **Compile/Publish Errors (3 failures)**:
+ - The primary issue across the failures is related to the use of `ScheduleAt::every_micros` versus `ScheduleAt::RepeatMicros`, which indicates a lack of clarity in the documentation about the correct method of using scheduled types.
+ - Another issue is the incorrect implementation of `pub` for some fields and missing `#[derive(SpacetimeType)]` for structs, which has led to schema mismatches.
+
+- **Other Failures (1 failure)**:
+ - The test `t_003_struct_in_table` has a mismatch where the expected reducer setup differs from what's provided. This highlights insufficient documentation around initial setup requirements for reducers.
+
+### 2. Recommendations
+- **Documentation Updates**:
+ - **Scheduled Types Documentation**: Enhance the section on scheduled types in the documentation to clarify the use of `ScheduleAt::every_micros` and `ScheduleAt::RepeatMicros`. Example for addition:
+ ```markdown
+ ### Scheduled Types
+ - Use `ScheduleAt::every_micros(interval)` for non-repeating intervals.
+ - Use `ScheduleAt::RepeatMicros(interval)` for repeating intervals. Ensure proper usage to avoid publishing errors.
+ ```
+
+ - **Section on Structs and Reducers**: Update the section dealing with struct fields to illustrate the necessity of using `pub` where it applies and clarifying how reducers must align:
+ ```markdown
+ ### Struct Definitions
+ - Struct fields must be marked as `pub` to ensure they are accessible within the SpacetimeDB context.
+ - Reducers must be defined properly; ensure that each reducer matches expected configurations in your schemas.
+ ```
+
+- **Example Code Alignment**: Revise example code throughout documentation to align with the latest syntax and ensure that all required attributes are included.
+
+### 3. Priority
+- **High Impact Fixes**:
+ 1. Scheduled Types Documentation (to prevent compile errors).
+ 2. Structs and Reducers Section (to ensure schema and function alignment).
+
+---
+
+## C# Failures
+
+### 1. Root Causes
+- **Table Naming Issues (19 failures)**:
+ - The primary issue causing the failures is the inconsistency in the use of table names (e.g., `entities` vs. `Entity`). Lack of clear guidelines on naming conventions has led to widespread discrepancies.
+
+- **Timeout Issues (3 failures)**:
+ - Additionally, the timeout failures indicate that certain processes aren’t being documented well in terms of what expectations exist for execution time and potential pitfalls leading to these issues.
+
+### 2. Recommendations
+- **Documentation Updates**:
+ - **Table Naming Conventions**: Introduce a comprehensive section specifying the naming conventions for tables. Example for addition:
+ ```markdown
+ ### Table Naming Conventions
+ - Table names should be singular and PascalCase (e.g., `User` instead of `users`).
+ - Ensure that when creating and querying tables, the names are consistently used to avoid schema parity issues.
+ ```
+
+ - **Timeout Handling Guidance**: Provide clearer information on how to handle potential timeout issues within operations:
+ ```markdown
+ ### Handling Timeouts
+ - If encountering timeout errors during transactions, consider optimizing the initial data load or query processes.
+ - Implement logging to help identify which part of your transaction is leading to the timeout.
+ ```
+
+### 3. Priority
+- **High Impact Fixes**:
+ 1. Table Naming Conventions (most immediate to fix widespread errors).
+ 2. Timeout Handling Guidance (to improve performance and reliability in operations).
+
+---
+
+This structured approach will help improve the accessibility and clarity of the SpacetimeDB documentation, directly addressing the root causes of current benchmark test failures.
diff --git a/docs/llms/docs-benchmark-comment.md b/docs/llms/docs-benchmark-comment.md
new file mode 100644
index 00000000000..00356a7efde
--- /dev/null
+++ b/docs/llms/docs-benchmark-comment.md
@@ -0,0 +1,14 @@
+## LLM Benchmark Results (ci-quickfix)
+
+| Language | Mode | Category | Tests Passed | Task Pass % |
+|----------|------|----------|--------------|-------------|
+| Rust | rustdoc_json | basics | 25/27 | 83.3% ⬇️ -8.3% |
+| Rust | rustdoc_json | schema | 29/34 | 80.0% |
+| Rust | rustdoc_json | **total** | 54/61 | **81.8%** ⬇️ -4.5% |
+| C# | docs | basics | 7/27 | 16.0% ⬇️ -84.0% |
+| C# | docs | schema | 18/34 | 48.5% ⬇️ -31.5% |
+| C# | docs | **total** | 25/61 | **30.8%** ⬇️ -60.2% |
+
+_Compared against master branch baseline_
+
+Generated at: 2026-01-16T19:34:55.608Z
diff --git a/docs/llms/docs-benchmark-details.json b/docs/llms/docs-benchmark-details.json
index 42f289091a9..e7d25c4bce3 100644
--- a/docs/llms/docs-benchmark-details.json
+++ b/docs/llms/docs-benchmark-details.json
@@ -19,7 +19,7 @@
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 1,
- "llm_output": "use spacetimedb::{reducer, ReducerContext};\n\n#[reducer]\npub fn empty_reducer_no_args(_ctx: &ReducerContext) {\n}\n\n#[reducer]\npub fn empty_reducer_with_int(_ctx: &ReducerContext, _count: i32) {\n}\n\n#[reducer]\npub fn empty_reducer_with_string(_ctx: &ReducerContext, _name: String) {\n}\n\n#[reducer]\npub fn empty_reducer_with_two_args(_ctx: &ReducerContext, _count: i32, _name: String) {\n}\n\n#[reducer]\npub fn empty_reducer_with_three_args(_ctx: &ReducerContext, _active: bool, _ratio: f32, _label: String) {\n}",
+ "llm_output": "use spacetimedb::ReducerContext;\n\n#[spacetimedb::reducer]\nfn empty_reducer_no_args(_ctx: &ReducerContext) {\n}\n\n#[spacetimedb::reducer]\nfn empty_reducer_with_int(_ctx: &ReducerContext, count: i32) {\n let _ = count;\n}\n\n#[spacetimedb::reducer]\nfn empty_reducer_with_string(_ctx: &ReducerContext, name: String) {\n let _ = name;\n}\n\n#[spacetimedb::reducer]\nfn empty_reducer_with_two_args(_ctx: &ReducerContext, count: i32, name: String) {\n let _ = (count, name);\n}\n\n#[spacetimedb::reducer]\nfn empty_reducer_with_three_args(_ctx: &ReducerContext, active: bool, ratio: f32, label: String) {\n let _ = (active, ratio, label);\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-000-empty-reducers-golden",
@@ -35,15 +35,15 @@
"llm_db": "basics-t-000-empty-reducers-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:32.994654009Z",
- "finished_at": "2026-01-16T00:57:50.266999019Z"
+ "started_at": "2026-01-17T02:28:16.167437181Z",
+ "finished_at": "2026-01-17T02:30:49.255347669Z"
},
"t_001_basic_tables": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -69,15 +69,15 @@
"llm_db": "basics-t-001-basic-tables-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:57:55.725635804Z",
- "finished_at": "2026-01-16T00:59:36.074188986Z"
+ "started_at": "2026-01-17T02:30:52.376540604Z",
+ "finished_at": "2026-01-17T02:32:37.875429994Z"
},
"t_002_scheduled_table": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -87,7 +87,7 @@
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 0,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, ScheduleAt, Table};\n\n#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\npub struct TickTimer {\n #[primary_key]\n #[auto_inc]\n scheduled_id: u64,\n scheduled_at: ScheduleAt,\n}\n\n#[reducer]\npub fn init(ctx: &ReducerContext) {\n if ctx.db.tick_timer().count() == 0 {\n ctx.db.tick_timer().insert(TickTimer {\n scheduled_id: 0,\n scheduled_at: ScheduleAt::repeat_micros(50_000),\n });\n }\n}\n\n#[reducer(scheduled)]\npub fn tick(_ctx: &ReducerContext, _row: &TickTimer) {\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};\n\n#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\npub struct TickTimer {\n #[primary_key]\n #[auto_inc]\n scheduled_id: u64,\n scheduled_at: ScheduleAt,\n}\n\n#[reducer(init)]\npub fn init(ctx: &ReducerContext) {\n if ctx.db.tick_timer().count() == 0 {\n ctx.db.tick_timer().insert(TickTimer {\n scheduled_id: 0,\n scheduled_at: ScheduleAt::from_now_micros(50_000),\n });\n }\n}\n\n#[reducer]\npub fn tick(ctx: &ReducerContext, scheduled_id: u64) {\n if let Some(mut row) = ctx.db.tick_timer().scheduled_id().find(scheduled_id) {\n row.scheduled_at = ScheduleAt::from_now_micros(50_000);\n let _ = ctx.db.tick_timer().scheduled_id().update(row);\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-002-scheduled-table-golden",
@@ -99,14 +99,14 @@
"pass": false,
"partial": 0.0,
"notes": {
- "error": "spacetime publish failed (exit=1)\n--- stderr ---\n\u001b[1m\u001b[32m Updating\u001b[0m crates.io index\n\u001b[1m\u001b[32m Locking\u001b[0m 72 packages to latest compatible versions\n\u001b[1m\u001b[36m Adding\u001b[0m generic-array v0.14.7 \u001b[1m\u001b[33m(available: v0.14.9)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-macro v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-sys v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-lib v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-primitives v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-sats v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[32m Compiling\u001b[0m proc-macro2 v1.0.105\n\u001b[1m\u001b[32m Compiling\u001b[0m unicode-ident v1.0.22\n\u001b[1m\u001b[32m Compiling\u001b[0m quote v1.0.43\n\u001b[1m\u001b[32m Compiling\u001b[0m version_check v0.9.5\n\u001b[1m\u001b[32m Compiling\u001b[0m typenum v1.19.0\n\u001b[1m\u001b[32m Compiling\u001b[0m autocfg v1.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m generic-array v0.14.7\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_core v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m num-traits v0.2.19\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m cfg-if v1.0.4\n\u001b[1m\u001b[32m Compiling\u001b[0m syn v2.0.114\n\u001b[1m\u001b[32m Compiling\u001b[0m shlex v1.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m either v1.15.0\n\u001b[1m\u001b[32m Compiling\u001b[0m serde v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m find-msvc-tools v0.1.7\n\u001b[1m\u001b[32m Compiling\u001b[0m zerocopy v0.8.33\n\u001b[1m\u001b[32m Compiling\u001b[0m itertools v0.12.1\n\u001b[1m\u001b[32m Compiling\u001b[0m cc v1.2.52\n\u001b[1m\u001b[32m Compiling\u001b[0m crypto-common v0.1.7\n\u001b[1m\u001b[32m Compiling\u001b[0m block-buffer v0.10.4\n\u001b[1m\u001b[32m Compiling\u001b[0m bitflags v2.10.0\n\u001b[1m\u001b[32m Compiling\u001b[0m nohash-hasher v0.2.0\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m anyhow v1.0.100\n\u001b[1m\u001b[32m Compiling\u001b[0m digest v0.10.7\n\u001b[1m\u001b[32m Compiling\u001b[0m blake3 v1.8.3\n\u001b[1m\u001b[32m Compiling\u001b[0m approx v0.3.2\n\u001b[1m\u001b[32m Compiling\u001b[0m getrandom v0.2.17\n\u001b[1m\u001b[32m Compiling\u001b[0m bytes v1.11.0\n\u001b[1m\u001b[32m Compiling\u001b[0m zmij v1.0.14\n\u001b[1m\u001b[32m Compiling\u001b[0m humantime v2.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.4.1\n\u001b[1m\u001b[32m Compiling\u001b[0m keccak v0.1.5\n\u001b[1m\u001b[32m Compiling\u001b[0m convert_case v0.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m enum-as-inner v0.6.1\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror-impl v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayvec v0.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-primitives v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m ppv-lite86 v0.2.21\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-macro v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m derive_more v0.99.20\n\u001b[1m\u001b[32m Compiling\u001b[0m sha3 v0.10.8\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_core v0.6.4\n\u001b[1m\u001b[32m Compiling\u001b[0m decorum v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m ethnum v1.5.2\n\u001b[1m\u001b[32m Compiling\u001b[0m chrono v0.4.43\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_json v1.0.149\n\u001b[1m\u001b[32m Compiling\u001b[0m constant_time_eq v0.4.2\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayref v0.3.9\n\u001b[1m\u001b[32m Compiling\u001b[0m second-stack v0.3.5\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-lib v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m bytemuck v1.24.0\n\u001b[1m\u001b[32m Compiling\u001b[0m itoa v1.0.17\n\u001b[1m\u001b[32m Compiling\u001b[0m smallvec v1.15.1\n\u001b[1m\u001b[32m Compiling\u001b[0m hex v0.4.3\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_chacha v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-sats v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m memchr v2.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m log v0.4.29\n\u001b[1m\u001b[32m Compiling\u001b[0m rand v0.8.5\n\u001b[1m\u001b[32m Compiling\u001b[0m http v1.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-sys v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m scoped-tls v1.0.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetime-module v0.1.0 (/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/basics/t_002_scheduled_table/rust/server/gpt-5/llm)\n\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: expected one of: `public`, `private`, `name`, `index`, `scheduled`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:4:28\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m4\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: expected one of: `init`, `client_connected`, `client_disconnected`, `update`, `name`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:22:11\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m22\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[reducer(scheduled)]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0422]\u001b[0m\u001b[0m\u001b[1m: cannot find struct, variant or union type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:36\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0412]\u001b[0m\u001b[0m\u001b[1m: cannot find type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:43\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: &TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:14:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m14\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m if ctx.db.tick_timer().count() == 0 {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:16\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no variant or associated item named `repeat_micros` found for enum `ScheduleAt` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:17:39\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m17\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m scheduled_at: ScheduleAt::repeat_micros(50_000),\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mvariant or associated item not found in `ScheduleAt`\u001b[0m\n\n\u001b[0m\u001b[1mSome errors have detailed explanations: E0412, E0422, E0599.\u001b[0m\n\u001b[0m\u001b[1mFor more information about an error, try `rustc --explain E0412`.\u001b[0m\n\u001b[1m\u001b[31merror\u001b[0m\u001b[1m:\u001b[0m could not compile `spacetime-module` (lib) due to 7 previous errors\nError: command [\"cargo\", \"build\", \"--config=net.git-fetch-with-cli=true\", \"--target=wasm32-unknown-unknown\", \"--release\", \"--message-format=json-render-diagnostics\"] exited with code 101\n\n--- stdout ---\n",
+ "error": "spacetime publish failed (exit=1)\n--- stderr ---\n\u001b[1m\u001b[32m Updating\u001b[0m crates.io index\n\u001b[1m\u001b[32m Locking\u001b[0m 72 packages to latest compatible versions\n\u001b[1m\u001b[36m Adding\u001b[0m generic-array v0.14.7 \u001b[1m\u001b[33m(available: v0.14.9)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-macro v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-sys v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-lib v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-primitives v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-sats v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[32m Compiling\u001b[0m proc-macro2 v1.0.105\n\u001b[1m\u001b[32m Compiling\u001b[0m quote v1.0.43\n\u001b[1m\u001b[32m Compiling\u001b[0m unicode-ident v1.0.22\n\u001b[1m\u001b[32m Compiling\u001b[0m typenum v1.19.0\n\u001b[1m\u001b[32m Compiling\u001b[0m version_check v0.9.5\n\u001b[1m\u001b[32m Compiling\u001b[0m autocfg v1.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m generic-array v0.14.7\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m num-traits v0.2.19\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_core v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m cfg-if v1.0.4\n\u001b[1m\u001b[32m Compiling\u001b[0m syn v2.0.114\n\u001b[1m\u001b[32m Compiling\u001b[0m either v1.15.0\n\u001b[1m\u001b[32m Compiling\u001b[0m find-msvc-tools v0.1.8\n\u001b[1m\u001b[32m Compiling\u001b[0m serde v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m shlex v1.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m zerocopy v0.8.33\n\u001b[1m\u001b[32m Compiling\u001b[0m cc v1.2.53\n\u001b[1m\u001b[32m Compiling\u001b[0m itertools v0.12.1\n\u001b[1m\u001b[32m Compiling\u001b[0m crypto-common v0.1.7\n\u001b[1m\u001b[32m Compiling\u001b[0m block-buffer v0.10.4\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m nohash-hasher v0.2.0\n\u001b[1m\u001b[32m Compiling\u001b[0m bitflags v2.10.0\n\u001b[1m\u001b[32m Compiling\u001b[0m anyhow v1.0.100\n\u001b[1m\u001b[32m Compiling\u001b[0m digest v0.10.7\n\u001b[1m\u001b[32m Compiling\u001b[0m blake3 v1.8.3\n\u001b[1m\u001b[32m Compiling\u001b[0m approx v0.3.2\n\u001b[1m\u001b[32m Compiling\u001b[0m getrandom v0.2.17\n\u001b[1m\u001b[32m Compiling\u001b[0m humantime v2.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m bytes v1.11.0\n\u001b[1m\u001b[32m Compiling\u001b[0m zmij v1.0.14\n\u001b[1m\u001b[32m Compiling\u001b[0m convert_case v0.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.4.1\n\u001b[1m\u001b[32m Compiling\u001b[0m enum-as-inner v0.6.1\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror-impl v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayvec v0.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-primitives v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m keccak v0.1.5\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-macro v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m sha3 v0.10.8\n\u001b[1m\u001b[32m Compiling\u001b[0m ppv-lite86 v0.2.21\n\u001b[1m\u001b[32m Compiling\u001b[0m derive_more v0.99.20\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_core v0.6.4\n\u001b[1m\u001b[32m Compiling\u001b[0m decorum v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m ethnum v1.5.2\n\u001b[1m\u001b[32m Compiling\u001b[0m chrono v0.4.43\n\u001b[1m\u001b[32m Compiling\u001b[0m constant_time_eq v0.4.2\n\u001b[1m\u001b[32m Compiling\u001b[0m bytemuck v1.24.0\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-lib v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m smallvec v1.15.1\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_json v1.0.149\n\u001b[1m\u001b[32m Compiling\u001b[0m itoa v1.0.17\n\u001b[1m\u001b[32m Compiling\u001b[0m hex v0.4.3\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayref v0.3.9\n\u001b[1m\u001b[32m Compiling\u001b[0m second-stack v0.3.5\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_chacha v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-sats v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m memchr v2.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m log v0.4.29\n\u001b[1m\u001b[32m Compiling\u001b[0m rand v0.8.5\n\u001b[1m\u001b[32m Compiling\u001b[0m http v1.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-sys v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m scoped-tls v1.0.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetime-module v0.1.0 (/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/basics/t_002_scheduled_table/rust/server/gpt-5/llm)\n\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: expected one of: `public`, `private`, `name`, `index`, `scheduled`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:4:28\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m4\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0422]\u001b[0m\u001b[0m\u001b[1m: cannot find struct, variant or union type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:36\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:14:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m14\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m if ctx.db.tick_timer().count() == 0 {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:16\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no variant or associated item named `from_now_micros` found for enum `ScheduleAt` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:17:39\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m17\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m scheduled_at: ScheduleAt::from_now_micros(50_000),\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mvariant or associated item not found in `ScheduleAt`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:24:35\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m24\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m if let Some(mut row) = ctx.db.tick_timer().scheduled_id().find(scheduled_id) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no variant or associated item named `from_now_micros` found for enum `ScheduleAt` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:25:40\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m25\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m row.scheduled_at = ScheduleAt::from_now_micros(50_000);\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mvariant or associated item not found in `ScheduleAt`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:26:24\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m26\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let _ = ctx.db.tick_timer().scheduled_id().update(row);\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1mSome errors have detailed explanations: E0422, E0599.\u001b[0m\n\u001b[0m\u001b[1mFor more information about an error, try `rustc --explain E0422`.\u001b[0m\n\u001b[1m\u001b[31merror\u001b[0m\u001b[1m:\u001b[0m could not compile `spacetime-module` (lib) due to 8 previous errors\nError: command [\"cargo\", \"build\", \"--config=net.git-fetch-with-cli=true\", \"--target=wasm32-unknown-unknown\", \"--release\", \"--message-format=json-render-diagnostics\"] exited with code 101\n\n--- stdout ---\n",
"phase": "build_or_publish"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:57:40.526882193Z",
- "finished_at": "2026-01-16T00:59:39.076539651Z"
+ "started_at": "2026-01-17T02:30:42.103317349Z",
+ "finished_at": "2026-01-17T02:32:41.832312901Z"
},
"t_003_struct_in_table": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -115,8 +115,8 @@
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 1,
- "passed_tests": 1,
- "llm_output": "use spacetimedb::{table, SpacetimeType};\n\n#[derive(SpacetimeType)]\npub struct Position {\n x: i32,\n y: i32,\n}\n\n#[table(name = entities)]\npub struct Entity {\n #[primary_key]\n id: i32,\n pos: Position,\n}",
+ "passed_tests": 0,
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType)]\npub struct Position {\n x: i32,\n y: i32,\n}\n\n#[table(name = entities)]\npub struct Entity {\n #[primary_key]\n id: i32,\n pos: Position,\n}\n\n#[reducer]\npub fn add_entity(ctx: &ReducerContext, id: i32, x: i32, y: i32) {\n ctx.db.entities().insert(Entity { id, pos: Position { x, y } });\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-003-struct-in-table-golden",
@@ -125,22 +125,27 @@
"work_dir_llm": "target/llm-runs/basics/t_003_struct_in_table/rust/server/gpt-5/llm",
"scorer_details": {
"schema_parity": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
"golden_db": "basics-t-003-struct-in-table-golden",
"llm_db": "basics-t-003-struct-in-table-gpt-5-llm",
- "reducers_diff": null,
- "reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "reducers_diff": {
+ "only_golden": [],
+ "only_llm": [
+ "add_entity()"
+ ]
+ },
+ "reducers_equal": false,
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:57:55.081651973Z",
- "finished_at": "2026-01-16T00:59:39.595370748Z"
+ "started_at": "2026-01-17T02:30:49.255385009Z",
+ "finished_at": "2026-01-17T02:32:37.661244218Z"
},
"t_004_insert": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -158,19 +163,6 @@
"work_dir_golden": "target/llm-runs/basics/t_004_insert/rust/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_004_insert/rust/server/gpt-5/llm",
"scorer_details": {
- "schema_parity": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "golden_db": "basics-t-004-insert-golden",
- "llm_db": "basics-t-004-insert-gpt-5-llm",
- "reducers_diff": null,
- "reducers_equal": true,
- "server": "http://127.0.0.1:39633",
- "tables_diff": null,
- "tables_equal": true
- }
- },
"data_parity_insert_user": {
"pass": true,
"partial": 1.0,
@@ -187,13 +179,26 @@
"llm_out": "id | name | age | active ----+---------+-----+-------- 1 | \"Alice\" | 30 | true",
"query": "SELECT id, name, age, active FROM users WHERE id=1",
"reducer": "insert_user",
- "server": "http://127.0.0.1:39633"
+ "server": "http://127.0.0.1:46587"
+ }
+ },
+ "schema_parity": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "golden_db": "basics-t-004-insert-golden",
+ "llm_db": "basics-t-004-insert-gpt-5-llm",
+ "reducers_diff": null,
+ "reducers_equal": true,
+ "server": "http://127.0.0.1:46587",
+ "tables_diff": null,
+ "tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:57:58.000448174Z",
- "finished_at": "2026-01-16T00:59:37.992407740Z"
+ "started_at": "2026-01-17T02:30:53.628765951Z",
+ "finished_at": "2026-01-17T02:32:37.475748331Z"
},
"t_005_update": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -203,7 +208,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 3,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[reducer]\npub fn update_user(ctx: &ReducerContext, id: i32, name: String, age: i32, active: bool) {\n let users = ctx.db.users();\n if users.id().find(id).is_some() {\n users.id().update(User { id, name, age, active });\n }\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[reducer]\npub fn update_user(ctx: &ReducerContext, id: i32, name: String, age: i32, active: bool) {\n let user = User { id, name, age, active };\n ctx.db.users().id().update(user);\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-005-update-golden",
@@ -227,7 +232,14 @@
"llm_out": "id | name | age | active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
"query": "SELECT id, name, age, active FROM users WHERE id=1",
"reducer": "update_user",
- "server": "http://127.0.0.1:39633"
+ "server": "http://127.0.0.1:46587"
+ }
+ },
+ "seed_users_row": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "sql": "INSERT INTO users(id, name, age, active) VALUES (1, 'Alice', 30, true)"
}
},
"schema_parity": {
@@ -238,22 +250,15 @@
"llm_db": "basics-t-005-update-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
- },
- "seed_users_row": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "sql": "INSERT INTO users(id, name, age, active) VALUES (1, 'Alice', 30, true)"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:40.398470793Z",
- "finished_at": "2026-01-16T00:57:57.999596161Z"
+ "started_at": "2026-01-17T02:28:20.539096519Z",
+ "finished_at": "2026-01-17T02:30:53.033151846Z"
},
"t_006_delete": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -278,6 +283,15 @@
"sql": "INSERT INTO users(id, name, age, active) VALUES (1, 'Alice', 30, true)"
}
},
+ "delete_user_count_zero": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "actual": 0,
+ "expected": 0,
+ "sql": "SELECT COUNT(*) AS n FROM users WHERE id=1"
+ }
+ },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -286,24 +300,15 @@
"llm_db": "basics-t-006-delete-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
- },
- "delete_user_count_zero": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 0,
- "expected": 0,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE id=1"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:36.775033097Z",
- "finished_at": "2026-01-16T00:57:55.081598062Z"
+ "started_at": "2026-01-17T02:28:17.746281172Z",
+ "finished_at": "2026-01-17T02:30:42.103247549Z"
},
"t_007_crud": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -313,7 +318,7 @@
"model_name": "GPT-5",
"total_tests": 4,
"passed_tests": 4,
- "llm_output": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[spacetimedb::reducer]\npub fn crud(ctx: &ReducerContext) {\n let users = ctx.db.users();\n\n let mut alice = users.insert(User {\n id: 1,\n name: \"Alice\".to_string(),\n age: 30,\n active: true,\n });\n\n users.insert(User {\n id: 2,\n name: \"Bob\".to_string(),\n age: 22,\n active: false,\n });\n\n alice.name = \"Alice2\".to_string();\n alice.age = 31;\n alice.active = false;\n users.id().update(alice);\n\n users.id().delete(&2);\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[reducer]\npub fn crud(ctx: &ReducerContext) {\n let mut u1 = ctx.db.users().insert(User {\n id: 1,\n name: \"Alice\".to_string(),\n age: 30,\n active: true,\n });\n ctx.db.users().insert(User {\n id: 2,\n name: \"Bob\".to_string(),\n age: 22,\n active: false,\n });\n u1.name = \"Alice2\".to_string();\n u1.age = 31;\n u1.active = false;\n let _ = ctx.db.users().id().update(u1);\n ctx.db.users().id().delete(&2);\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-007-crud-golden",
@@ -321,55 +326,55 @@
"work_dir_golden": "target/llm-runs/basics/t_007_crud/rust/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_007_crud/rust/server/gpt-5/llm",
"scorer_details": {
- "crud_total_count_one": {
+ "crud_row_id2_deleted": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM users"
+ "actual": 0,
+ "expected": 0,
+ "sql": "SELECT COUNT(*) AS n FROM users WHERE id=2"
}
},
- "crud_row_id1_parity": {
+ "schema_parity": {
"pass": true,
"partial": 1.0,
"notes": {
- "args": [],
"golden_db": "basics-t-007-crud-golden",
- "golden_out": "id | name | age | active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
"llm_db": "basics-t-007-crud-gpt-5-llm",
- "llm_out": "id | name | age | active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
- "query": "SELECT id, name, age, active FROM users WHERE id=1",
- "reducer": "crud",
- "server": "http://127.0.0.1:39633"
+ "reducers_diff": null,
+ "reducers_equal": true,
+ "server": "http://127.0.0.1:46587",
+ "tables_diff": null,
+ "tables_equal": true
}
},
- "crud_row_id2_deleted": {
+ "crud_total_count_one": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 0,
- "expected": 0,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE id=2"
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM users"
}
},
- "schema_parity": {
+ "crud_row_id1_parity": {
"pass": true,
"partial": 1.0,
"notes": {
+ "args": [],
"golden_db": "basics-t-007-crud-golden",
+ "golden_out": "id | name | age | active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
"llm_db": "basics-t-007-crud-gpt-5-llm",
- "reducers_diff": null,
- "reducers_equal": true,
- "server": "http://127.0.0.1:39633",
- "tables_diff": null,
- "tables_equal": true
+ "llm_out": "id | name | age | active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
+ "query": "SELECT id, name, age, active FROM users WHERE id=1",
+ "reducer": "crud",
+ "server": "http://127.0.0.1:46587"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:35.376875489Z",
- "finished_at": "2026-01-16T00:57:57.388761441Z"
+ "started_at": "2026-01-17T02:28:16.167798252Z",
+ "finished_at": "2026-01-17T02:30:53.400172120Z"
},
"t_008_index_lookup": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -379,7 +384,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 3,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n name: String,\n}\n\n#[reducer]\npub fn lookup_user_name(ctx: &ReducerContext, id: i32) {\n if let Some(user) = ctx.db.users().id().find(id) {\n ctx.db.results().insert(ResultRow { id: user.id, name: user.name });\n }\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n name: String,\n}\n\n#[reducer]\nfn lookup_user_name(ctx: &ReducerContext, id: i32) {\n if let Some(u) = ctx.db.users().id().find(id) {\n ctx.db.results().insert(ResultRow { id: u.id, name: u.name });\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-008-index-lookup-golden",
@@ -400,7 +405,7 @@
"llm_out": "id | name ----+--------- 1 | \"Alice\"",
"query": "SELECT id, name FROM results WHERE id=1",
"reducer": "lookup_user_name",
- "server": "http://127.0.0.1:39633"
+ "server": "http://127.0.0.1:46587"
}
},
"seed_user_row": {
@@ -418,15 +423,15 @@
"llm_db": "basics-t-008-index-lookup-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:40.201797723Z",
- "finished_at": "2026-01-16T00:57:58.991030144Z"
+ "started_at": "2026-01-17T02:28:17.748044021Z",
+ "finished_at": "2026-01-17T02:30:54.245888026Z"
},
"t_009_init": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -436,7 +441,7 @@
"model_name": "GPT-5",
"total_tests": 4,
"passed_tests": 4,
- "llm_output": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[spacetimedb::reducer(init)]\npub fn init(ctx: &ReducerContext) {\n ctx.db.users().insert(User {\n id: 1,\n name: \"Alice\".to_string(),\n age: 30,\n active: true,\n });\n ctx.db.users().insert(User {\n id: 2,\n name: \"Bob\".to_string(),\n age: 22,\n active: false,\n });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n id: i32,\n name: String,\n age: i32,\n active: bool,\n}\n\n#[reducer(init)]\npub fn init(ctx: &ReducerContext) {\n ctx.db.users().insert(User { id: 1, name: \"Alice\".to_string(), age: 30, active: true });\n ctx.db.users().insert(User { id: 2, name: \"Bob\".to_string(), age: 22, active: false });\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-009-init-golden",
@@ -453,6 +458,15 @@
"sql": "SELECT COUNT(*) AS n FROM users"
}
},
+ "init_seed_bob": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM users WHERE id=2 AND name='Bob' AND age=22 AND active=false"
+ }
+ },
"init_seed_alice": {
"pass": true,
"partial": 1.0,
@@ -470,24 +484,15 @@
"llm_db": "basics-t-009-init-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
- },
- "init_seed_bob": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE id=2 AND name='Bob' AND age=22 AND active=false"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:40.398058582Z",
- "finished_at": "2026-01-16T00:57:59.118749309Z"
+ "started_at": "2026-01-17T02:28:17.930019173Z",
+ "finished_at": "2026-01-17T02:30:41.209696143Z"
},
"t_010_connect": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -497,7 +502,7 @@
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 1,
- "llm_output": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(name = events)]\npub struct Event {\n #[primary_key]\n #[auto_inc]\n id: i32,\n kind: String,\n}\n\n#[spacetimedb::reducer(client_connected)]\npub fn client_connected(ctx: &ReducerContext) {\n ctx.db.events().insert(Event { id: 0, kind: \"connected\".to_string() });\n}\n\n#[spacetimedb::reducer(client_disconnected)]\npub fn client_disconnected(ctx: &ReducerContext) {\n ctx.db.events().insert(Event { id: 0, kind: \"disconnected\".to_string() });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = events)]\npub struct Event {\n #[primary_key]\n #[auto_inc]\n id: i32,\n kind: String,\n}\n\n#[reducer(client_connected)]\npub fn client_connected(ctx: &ReducerContext) {\n ctx.db.events().insert(Event { id: 0, kind: \"connected\".to_string() });\n}\n\n#[reducer(client_disconnected)]\npub fn client_disconnected(ctx: &ReducerContext) {\n ctx.db.events().insert(Event { id: 0, kind: \"disconnected\".to_string() });\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-010-connect-golden",
@@ -513,15 +518,15 @@
"llm_db": "basics-t-010-connect-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:57:57.999646665Z",
- "finished_at": "2026-01-16T00:59:40.062984353Z"
+ "started_at": "2026-01-17T02:30:53.626651147Z",
+ "finished_at": "2026-01-17T02:32:36.216348757Z"
},
"t_011_helper_function": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -531,7 +536,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 3,
- "llm_output": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n sum: i32,\n}\n\nfn add(a: i32, b: i32) -> i32 {\n a + b\n}\n\n#[spacetimedb::reducer]\npub fn compute_sum(ctx: &ReducerContext, id: i32, a: i32, b: i32) {\n let sum = add(a, b);\n ctx.db.results().insert(ResultRow { id, sum });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n sum: i32,\n}\n\nfn add(a: i32, b: i32) -> i32 {\n a + b\n}\n\n#[reducer]\nfn compute_sum(ctx: &ReducerContext, id: i32, a: i32, b: i32) {\n let sum = add(a, b);\n ctx.db.results().insert(ResultRow { id, sum });\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-011-helper-function-golden",
@@ -554,7 +559,7 @@
"llm_out": "id | sum ----+----- 1 | 5",
"query": "SELECT id, sum FROM results WHERE id=1",
"reducer": "compute_sum",
- "server": "http://127.0.0.1:39633"
+ "server": "http://127.0.0.1:46587"
}
},
"schema_parity": {
@@ -565,7 +570,7 @@
"llm_db": "basics-t-011-helper-function-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
@@ -581,8 +586,8 @@
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:57:50.267037300Z",
- "finished_at": "2026-01-16T00:59:13.310786115Z"
+ "started_at": "2026-01-17T02:30:42.105283707Z",
+ "finished_at": "2026-01-17T02:31:52.745496413Z"
},
"t_012_spacetime_product_type": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -592,7 +597,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 2,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType)]\nstruct Score {\n left: i32,\n right: i32,\n}\n\n#[table(name = results)]\nstruct ResultRow {\n #[primary_key]\n id: i32,\n value: Score,\n}\n\n#[reducer]\nfn set_score(ctx: &ReducerContext, id: i32, left: i32, right: i32) {\n ctx.db.results().insert(ResultRow {\n id,\n value: Score { left, right },\n });\n}",
+ "llm_output": "use spacetimedb::{ReducerContext, Table};\n\n#[derive(spacetimedb::SpacetimeType)]\npub struct Score {\n left: i32,\n right: i32,\n}\n\n#[spacetimedb::table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n value: Score,\n}\n\n#[spacetimedb::reducer]\npub fn set_score(ctx: &ReducerContext, id: i32, left: i32, right: i32) {\n ctx.db.results().insert(ResultRow { id, value: Score { left, right } });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-012-spacetime-product-type-golden",
@@ -609,6 +614,14 @@
"sql": "SELECT COUNT(*) AS n FROM results WHERE id=1"
}
},
+ "product_type_row_parity": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:46587/v1/database/c20064919d5938e3c53955f56eff54581cc88db76e6afe6cedff286135db8edb/sql)\n",
+ "phase": "sql_golden"
+ }
+ },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -617,23 +630,15 @@
"llm_db": "schema-t-012-spacetime-product-type-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
- },
- "product_type_row_parity": {
- "pass": false,
- "partial": 0.0,
- "notes": {
- "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:39633/v1/database/c20053a41b3acf73960cda9a96c51b51aa6f343d021f0109b2f2da5e9537d18d/sql)\n",
- "phase": "sql_golden"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.557696880Z",
- "finished_at": "2026-01-16T00:55:24.481587160Z"
+ "started_at": "2026-01-17T02:25:38.992696821Z",
+ "finished_at": "2026-01-17T02:28:16.167373502Z"
},
"t_013_spacetime_sum_type": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -643,7 +648,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 3,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType)]\npub struct Rect {\n width: i32,\n height: i32,\n}\n\n#[derive(SpacetimeType)]\npub enum Shape {\n Circle(i32),\n Rectangle(Rect),\n}\n\n#[table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n value: Shape,\n}\n\n#[reducer]\npub fn set_circle(ctx: &ReducerContext, id: i32, radius: i32) {\n ctx.db.results().insert(ResultRow { id, value: Shape::Circle(radius) });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, SpacetimeType, Table};\n\n#[derive(SpacetimeType)]\npub struct Rect {\n width: i32,\n height: i32,\n}\n\n#[derive(SpacetimeType)]\npub enum Shape {\n Circle(i32),\n Rectangle(Rect),\n}\n\n#[table(name = results)]\npub struct ResultRow {\n #[primary_key]\n id: i32,\n value: Shape,\n}\n\n#[reducer]\npub fn set_circle(ctx: &ReducerContext, id: i32, radius: i32) {\n ctx.db.results().id().delete(&id);\n ctx.db.results().insert(ResultRow { id, value: Shape::Circle(radius) });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-013-spacetime-sum-type-golden",
@@ -659,11 +664,20 @@
"llm_db": "schema-t-013-spacetime-sum-type-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
},
+ "sum_type_row_count": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM results WHERE id=1"
+ }
+ },
"sum_type_row_parity": {
"pass": true,
"partial": 1.0,
@@ -678,22 +692,13 @@
"llm_out": "id | value ----+--------------- 1 | (Circle = 10)",
"query": "SELECT id, value FROM results WHERE id=1",
"reducer": "set_circle",
- "server": "http://127.0.0.1:39633"
- }
- },
- "sum_type_row_count": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM results WHERE id=1"
+ "server": "http://127.0.0.1:46587"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:29.605929261Z",
- "finished_at": "2026-01-16T00:57:40.526817081Z"
+ "started_at": "2026-01-17T02:28:12.198644503Z",
+ "finished_at": "2026-01-17T02:30:53.626583041Z"
},
"t_014_elementary_columns": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -703,7 +708,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 3,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = primitives)]\npub struct Primitive {\n #[primary_key]\n id: i32,\n count: i32,\n total: i64,\n price: f32,\n ratio: f64,\n active: bool,\n name: String,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n ctx.db.primitives().insert(Primitive {\n id: 1,\n count: 2,\n total: 3_000_000_000,\n price: 1.5,\n ratio: 2.25,\n active: true,\n name: \"Alice\".to_string(),\n });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = primitives)]\npub struct Primitive {\n #[primary_key]\n id: i32,\n count: i32,\n total: i64,\n price: f32,\n ratio: f64,\n active: bool,\n name: String,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n ctx.db.primitives().insert(Primitive {\n id: 1,\n count: 2,\n total: 3_000_000_000i64,\n price: 1.5f32,\n ratio: 2.25f64,\n active: true,\n name: \"Alice\".to_string(),\n });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-014-elementary-columns-golden",
@@ -728,7 +733,7 @@
"llm_db": "schema-t-014-elementary-columns-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
@@ -744,13 +749,13 @@
"llm_out": "id | count | total | price | ratio | active | name ----+-------+------------+-------+-------+--------+--------- 1 | 2 | 3000000000 | 1.5 | 2.25 | true | \"Alice\"",
"query": "SELECT id, count, total, price, ratio, active, name FROM primitives WHERE id=1",
"reducer": "seed",
- "server": "http://127.0.0.1:39633"
+ "server": "http://127.0.0.1:46587"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.557387542Z",
- "finished_at": "2026-01-16T00:55:29.605867355Z"
+ "started_at": "2026-01-17T02:25:38.992406952Z",
+ "finished_at": "2026-01-17T02:28:06.754249465Z"
},
"t_015_product_type_columns": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -760,7 +765,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 2,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType)]\npub struct Address {\n street: String,\n zip: i32,\n}\n\n#[derive(SpacetimeType)]\npub struct Position {\n x: i32,\n y: i32,\n}\n\n#[table(name = profiles)]\npub struct Profile {\n #[primary_key]\n id: i32,\n home: Address,\n work: Address,\n pos: Position,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n ctx.db.profiles().insert(Profile {\n id: 1,\n home: Address {\n street: \"1 Main\".to_string(),\n zip: 11111,\n },\n work: Address {\n street: \"2 Broad\".to_string(),\n zip: 22222,\n },\n pos: Position { x: 7, y: 9 },\n });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType, Clone)]\npub struct Address {\n street: String,\n zip: i32,\n}\n\n#[derive(SpacetimeType, Clone)]\npub struct Position {\n x: i32,\n y: i32,\n}\n\n#[table(name = profiles)]\npub struct Profile {\n #[primary_key]\n id: i32,\n home: Address,\n work: Address,\n pos: Position,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let home = Address { street: \"1 Main\".to_string(), zip: 11111 };\n let work = Address { street: \"2 Broad\".to_string(), zip: 22222 };\n let pos = Position { x: 7, y: 9 };\n ctx.db.profiles().insert(Profile { id: 1, home, work, pos });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-015-product-type-columns-golden",
@@ -768,12 +773,13 @@
"work_dir_golden": "target/llm-runs/schema/t_015_product_type_columns/rust/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_015_product_type_columns/rust/server/gpt-5/llm",
"scorer_details": {
- "product_type_columns_row_parity": {
- "pass": false,
- "partial": 0.0,
+ "product_type_columns_row_count": {
+ "pass": true,
+ "partial": 1.0,
"notes": {
- "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `profiles`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:39633/v1/database/c2009d57e3266caa27aacc56f0c67f35ff7831a1e8e050f94ea2e7168e4d2d29/sql)\n",
- "phase": "sql_golden"
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM profiles WHERE id=1"
}
},
"schema_parity": {
@@ -784,24 +790,23 @@
"llm_db": "schema-t-015-product-type-columns-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
},
- "product_type_columns_row_count": {
- "pass": true,
- "partial": 1.0,
+ "product_type_columns_row_parity": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM profiles WHERE id=1"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `profiles`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:46587/v1/database/c200f79f71d0434e7e09b201e09674fe9fc25e018d5ba8eae68ccedefeafa4b2/sql)\n",
+ "phase": "sql_golden"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.558011008Z",
- "finished_at": "2026-01-16T00:55:32.994601591Z"
+ "started_at": "2026-01-17T02:25:38.993007169Z",
+ "finished_at": "2026-01-17T02:28:12.198565927Z"
},
"t_016_sum_type_columns": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -811,7 +816,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 3,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType, Clone, Debug)]\nstruct Rect {\n width: i32,\n height: i32,\n}\n\n#[derive(SpacetimeType, Clone, Debug)]\nenum Shape {\n Circle(i32),\n Rectangle(Rect),\n}\n\n#[table(name = drawings)]\nstruct Drawing {\n #[primary_key]\n id: i32,\n a: Shape,\n b: Shape,\n}\n\n#[reducer]\nfn seed(ctx: &ReducerContext) {\n let drawings = ctx.db.drawings();\n let ids: Vec = drawings.iter().map(|d| d.id).collect();\n for id in ids {\n drawings.id().delete(&id);\n }\n drawings.insert(Drawing {\n id: 1,\n a: Shape::Circle(10),\n b: Shape::Rectangle(Rect { width: 4, height: 6 }),\n });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};\n\n#[derive(SpacetimeType)]\npub struct Rect {\n width: i32,\n height: i32,\n}\n\n#[derive(SpacetimeType)]\npub enum Shape {\n Circle(i32),\n Rectangle(Rect),\n}\n\n#[table(name = drawings)]\npub struct Drawing {\n #[primary_key]\n id: i32,\n a: Shape,\n b: Shape,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let drawings = ctx.db.drawings();\n if drawings.count() == 0 {\n drawings.insert(Drawing {\n id: 1,\n a: Shape::Circle(10),\n b: Shape::Rectangle(Rect { width: 4, height: 6 }),\n });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-016-sum-type-columns-golden",
@@ -827,20 +832,11 @@
"llm_db": "schema-t-016-sum-type-columns-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
},
- "sum_type_columns_row_count": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM drawings WHERE id=1"
- }
- },
"sum_type_columns_row_parity": {
"pass": true,
"partial": 1.0,
@@ -852,13 +848,22 @@
"llm_out": "id | a | b ----+---------------+--------------------------------------- 1 | (Circle = 10) | (Rectangle = (width = 4, height = 6))",
"query": "SELECT id, a, b FROM drawings WHERE id=1",
"reducer": "seed",
- "server": "http://127.0.0.1:39633"
+ "server": "http://127.0.0.1:46587"
+ }
+ },
+ "sum_type_columns_row_count": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM drawings WHERE id=1"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:55:24.481644858Z",
- "finished_at": "2026-01-16T00:57:55.725583576Z"
+ "started_at": "2026-01-17T02:28:06.754312132Z",
+ "finished_at": "2026-01-17T02:30:52.376483288Z"
},
"t_017_scheduled_columns": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -868,7 +873,7 @@
"model_name": "GPT-5",
"total_tests": 2,
"passed_tests": 0,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};\n\n#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\npub struct TickTimer {\n #[primary_key]\n #[auto_inc]\n scheduled_id: u64,\n scheduled_at: ScheduleAt,\n}\n\n#[reducer(init)]\npub fn init(ctx: &ReducerContext) {\n if ctx.db.tick_timer().count() == 0 {\n ctx.db.tick_timer().insert(TickTimer {\n scheduled_id: 0,\n scheduled_at: ScheduleAt::repeat_micros(50_000),\n });\n }\n}\n\n#[reducer]\npub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};\n\n#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\npub struct TickTimer {\n #[primary_key]\n #[auto_inc]\n scheduled_id: u64,\n scheduled_at: ScheduleAt,\n}\n\n#[reducer(init)]\npub fn init(ctx: &ReducerContext) {\n if ctx.db.tick_timer().count() == 0 {\n ctx.db.tick_timer().insert(TickTimer {\n scheduled_id: 0,\n scheduled_at: ScheduleAt::RepeatMicros(50_000),\n });\n }\n}\n\n#[reducer]\npub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\n // Scheduled reducer invoked with the scheduled row.\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-017-scheduled-columns-golden",
@@ -880,14 +885,14 @@
"pass": false,
"partial": 0.0,
"notes": {
- "error": "spacetime publish failed (exit=1)\n--- stderr ---\n\u001b[1m\u001b[32m Updating\u001b[0m crates.io index\n\u001b[1m\u001b[32m Locking\u001b[0m 72 packages to latest compatible versions\n\u001b[1m\u001b[36m Adding\u001b[0m generic-array v0.14.7 \u001b[1m\u001b[33m(available: v0.14.9)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-macro v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-sys v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-lib v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-primitives v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-sats v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[32m Compiling\u001b[0m proc-macro2 v1.0.105\n\u001b[1m\u001b[32m Compiling\u001b[0m unicode-ident v1.0.22\n\u001b[1m\u001b[32m Compiling\u001b[0m quote v1.0.43\n\u001b[1m\u001b[32m Compiling\u001b[0m typenum v1.19.0\n\u001b[1m\u001b[32m Compiling\u001b[0m version_check v0.9.5\n\u001b[1m\u001b[32m Compiling\u001b[0m autocfg v1.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m generic-array v0.14.7\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_core v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m num-traits v0.2.19\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m cfg-if v1.0.4\n\u001b[1m\u001b[32m Compiling\u001b[0m syn v2.0.114\n\u001b[1m\u001b[32m Compiling\u001b[0m zerocopy v0.8.33\n\u001b[1m\u001b[32m Compiling\u001b[0m either v1.15.0\n\u001b[1m\u001b[32m Compiling\u001b[0m shlex v1.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m find-msvc-tools v0.1.7\n\u001b[1m\u001b[32m Compiling\u001b[0m serde v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m itertools v0.12.1\n\u001b[1m\u001b[32m Compiling\u001b[0m cc v1.2.52\n\u001b[1m\u001b[32m Compiling\u001b[0m block-buffer v0.10.4\n\u001b[1m\u001b[32m Compiling\u001b[0m crypto-common v0.1.7\n\u001b[1m\u001b[32m Compiling\u001b[0m anyhow v1.0.100\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m bitflags v2.10.0\n\u001b[1m\u001b[32m Compiling\u001b[0m nohash-hasher v0.2.0\n\u001b[1m\u001b[32m Compiling\u001b[0m digest v0.10.7\n\u001b[1m\u001b[32m Compiling\u001b[0m blake3 v1.8.3\n\u001b[1m\u001b[32m Compiling\u001b[0m approx v0.3.2\n\u001b[1m\u001b[32m Compiling\u001b[0m getrandom v0.2.17\n\u001b[1m\u001b[32m Compiling\u001b[0m zmij v1.0.14\n\u001b[1m\u001b[32m Compiling\u001b[0m humantime v2.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayvec v0.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m bytes v1.11.0\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.4.1\n\u001b[1m\u001b[32m Compiling\u001b[0m convert_case v0.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m keccak v0.1.5\n\u001b[1m\u001b[32m Compiling\u001b[0m sha3 v0.10.8\n\u001b[1m\u001b[32m Compiling\u001b[0m enum-as-inner v0.6.1\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror-impl v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m ppv-lite86 v0.2.21\n\u001b[1m\u001b[32m Compiling\u001b[0m derive_more v0.99.20\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-primitives v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-macro v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_core v0.6.4\n\u001b[1m\u001b[32m Compiling\u001b[0m decorum v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m ethnum v1.5.2\n\u001b[1m\u001b[32m Compiling\u001b[0m chrono v0.4.43\n\u001b[1m\u001b[32m Compiling\u001b[0m itoa v1.0.17\n\u001b[1m\u001b[32m Compiling\u001b[0m smallvec v1.15.1\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayref v0.3.9\n\u001b[1m\u001b[32m Compiling\u001b[0m constant_time_eq v0.4.2\n\u001b[1m\u001b[32m Compiling\u001b[0m hex v0.4.3\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-lib v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m second-stack v0.3.5\n\u001b[1m\u001b[32m Compiling\u001b[0m bytemuck v1.24.0\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_json v1.0.149\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_chacha v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-sats v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m log v0.4.29\n\u001b[1m\u001b[32m Compiling\u001b[0m memchr v2.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m rand v0.8.5\n\u001b[1m\u001b[32m Compiling\u001b[0m http v1.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-sys v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m scoped-tls v1.0.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetime-module v0.1.0 (/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_017_scheduled_columns/rust/server/gpt-5/llm)\n\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: expected one of: `public`, `private`, `name`, `index`, `scheduled`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:4:28\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m4\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0422]\u001b[0m\u001b[0m\u001b[1m: cannot find struct, variant or union type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:36\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0412]\u001b[0m\u001b[0m\u001b[1m: cannot find type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:42\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:14:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m14\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m if ctx.db.tick_timer().count() == 0 {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:16\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no variant or associated item named `repeat_micros` found for enum `ScheduleAt` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:17:39\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m17\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m scheduled_at: ScheduleAt::repeat_micros(50_000),\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mvariant or associated item not found in `ScheduleAt`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0277]\u001b[0m\u001b[0m\u001b[1m: invalid reducer signature\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:8\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m22\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[reducer]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mrequired by a bound introduced by this call\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mthis reducer signature is not valid\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mhelp\u001b[0m\u001b[0m: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext, {type error}) {tick}`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: reducer signatures must match the following pattern:\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: where each `Ti` type implements `SpacetimeType`.\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[0m: required by a bound in `register_reducer`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spacetimedb-1.11.1/src/rt.rs:746:81\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m746\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10m^^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10mrequired by this bound in `register_reducer`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0277]\u001b[0m\u001b[0m\u001b[1m: invalid reducer signature\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:8\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m22\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[reducer]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mrequired by a bound introduced by this call\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mthis reducer signature is not valid\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mhelp\u001b[0m\u001b[0m: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext, {type error}) {tick}`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: reducer signatures must match the following pattern:\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: where each `Ti` type implements `SpacetimeType`.\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[0m: required by a bound in `invoke_reducer`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spacetimedb-1.11.1/src/rt.rs:45:19\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m44\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn invoke_reducer<'a, A: Args<'a>>(\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--------------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mrequired by a bound in this function\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m45\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m reducer: impl Reducer<'a, A>,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10m^^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10mrequired by this bound in `invoke_reducer`\u001b[0m\n\n\u001b[0m\u001b[1mSome errors have detailed explanations: E0277, E0412, E0422, E0599.\u001b[0m\n\u001b[0m\u001b[1mFor more information about an error, try `rustc --explain E0277`.\u001b[0m\n\u001b[1m\u001b[31merror\u001b[0m\u001b[1m:\u001b[0m could not compile `spacetime-module` (lib) due to 8 previous errors\nError: command [\"cargo\", \"build\", \"--config=net.git-fetch-with-cli=true\", \"--target=wasm32-unknown-unknown\", \"--release\", \"--message-format=json-render-diagnostics\"] exited with code 101\n\n--- stdout ---\n",
+ "error": "spacetime publish failed (exit=1)\n--- stderr ---\n\u001b[1m\u001b[32m Updating\u001b[0m crates.io index\n\u001b[1m\u001b[32m Locking\u001b[0m 72 packages to latest compatible versions\n\u001b[1m\u001b[36m Adding\u001b[0m generic-array v0.14.7 \u001b[1m\u001b[33m(available: v0.14.9)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-macro v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-bindings-sys v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-lib v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-primitives v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[36m Adding\u001b[0m spacetimedb-sats v1.11.1 \u001b[1m\u001b[33m(available: v1.11.3)\u001b[0m\n\u001b[1m\u001b[32m Compiling\u001b[0m proc-macro2 v1.0.105\n\u001b[1m\u001b[32m Compiling\u001b[0m quote v1.0.43\n\u001b[1m\u001b[32m Compiling\u001b[0m unicode-ident v1.0.22\n\u001b[1m\u001b[32m Compiling\u001b[0m version_check v0.9.5\n\u001b[1m\u001b[32m Compiling\u001b[0m typenum v1.19.0\n\u001b[1m\u001b[32m Compiling\u001b[0m autocfg v1.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m generic-array v0.14.7\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.5.0\n\u001b[1m\u001b[32m Compiling\u001b[0m num-traits v0.2.19\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_core v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m cfg-if v1.0.4\n\u001b[1m\u001b[32m Compiling\u001b[0m syn v2.0.114\n\u001b[1m\u001b[32m Compiling\u001b[0m shlex v1.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m serde v1.0.228\n\u001b[1m\u001b[32m Compiling\u001b[0m find-msvc-tools v0.1.8\n\u001b[1m\u001b[32m Compiling\u001b[0m zerocopy v0.8.33\n\u001b[1m\u001b[32m Compiling\u001b[0m either v1.15.0\n\u001b[1m\u001b[32m Compiling\u001b[0m itertools v0.12.1\n\u001b[1m\u001b[32m Compiling\u001b[0m cc v1.2.53\n\u001b[1m\u001b[32m Compiling\u001b[0m crypto-common v0.1.7\n\u001b[1m\u001b[32m Compiling\u001b[0m block-buffer v0.10.4\n\u001b[1m\u001b[32m Compiling\u001b[0m nohash-hasher v0.2.0\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m bitflags v2.10.0\n\u001b[1m\u001b[32m Compiling\u001b[0m anyhow v1.0.100\n\u001b[1m\u001b[32m Compiling\u001b[0m digest v0.10.7\n\u001b[1m\u001b[32m Compiling\u001b[0m approx v0.3.2\n\u001b[1m\u001b[32m Compiling\u001b[0m getrandom v0.2.17\n\u001b[1m\u001b[32m Compiling\u001b[0m heck v0.4.1\n\u001b[1m\u001b[32m Compiling\u001b[0m humantime v2.3.0\n\u001b[1m\u001b[32m Compiling\u001b[0m blake3 v1.8.3\n\u001b[1m\u001b[32m Compiling\u001b[0m bytes v1.11.0\n\u001b[1m\u001b[32m Compiling\u001b[0m keccak v0.1.5\n\u001b[1m\u001b[32m Compiling\u001b[0m zmij v1.0.14\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayvec v0.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m convert_case v0.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m sha3 v0.10.8\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_core v0.6.4\n\u001b[1m\u001b[32m Compiling\u001b[0m enum-as-inner v0.6.1\n\u001b[1m\u001b[32m Compiling\u001b[0m thiserror-impl v1.0.69\n\u001b[1m\u001b[32m Compiling\u001b[0m ppv-lite86 v0.2.21\n\u001b[1m\u001b[32m Compiling\u001b[0m derive_more v0.99.20\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-primitives v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-macro v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m decorum v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m ethnum v1.5.2\n\u001b[1m\u001b[32m Compiling\u001b[0m chrono v0.4.43\n\u001b[1m\u001b[32m Compiling\u001b[0m bytemuck v1.24.0\n\u001b[1m\u001b[32m Compiling\u001b[0m arrayref v0.3.9\n\u001b[1m\u001b[32m Compiling\u001b[0m hex v0.4.3\n\u001b[1m\u001b[32m Compiling\u001b[0m smallvec v1.15.1\n\u001b[1m\u001b[32m Compiling\u001b[0m constant_time_eq v0.4.2\n\u001b[1m\u001b[32m Compiling\u001b[0m serde_json v1.0.149\n\u001b[1m\u001b[32m Compiling\u001b[0m second-stack v0.3.5\n\u001b[1m\u001b[32m Compiling\u001b[0m itoa v1.0.17\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-lib v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m rand_chacha v0.3.1\n\u001b[1m\u001b[32m Compiling\u001b[0m log v0.4.29\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-sats v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m memchr v2.7.6\n\u001b[1m\u001b[32m Compiling\u001b[0m rand v0.8.5\n\u001b[1m\u001b[32m Compiling\u001b[0m http v1.4.0\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb-bindings-sys v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m scoped-tls v1.0.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetimedb v1.11.1\n\u001b[1m\u001b[32m Compiling\u001b[0m spacetime-module v0.1.0 (/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_017_scheduled_columns/rust/server/gpt-5/llm)\n\u001b[0m\u001b[1m\u001b[38;5;9merror\u001b[0m\u001b[0m\u001b[1m: expected one of: `public`, `private`, `name`, `index`, `scheduled`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:4:28\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m4\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0422]\u001b[0m\u001b[0m\u001b[1m: cannot find struct, variant or union type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:36\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0412]\u001b[0m\u001b[0m\u001b[1m: cannot find type `TickTimer` in this scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:42\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mnot found in this scope\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:14:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m14\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m if ctx.db.tick_timer().count() == 0 {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no method named `tick_timer` found for struct `Local` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:15:16\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m15\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m ctx.db.tick_timer().insert(TickTimer {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mmethod not found in `Local`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0599]\u001b[0m\u001b[0m\u001b[1m: no variant or associated item named `RepeatMicros` found for enum `ScheduleAt` in the current scope\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:17:39\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m17\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m scheduled_at: ScheduleAt::RepeatMicros(50_000),\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mvariant or associated item not found in `ScheduleAt`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0277]\u001b[0m\u001b[0m\u001b[1m: invalid reducer signature\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:8\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m22\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[reducer]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mrequired by a bound introduced by this call\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mthis reducer signature is not valid\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mhelp\u001b[0m\u001b[0m: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext, {type error}) {tick}`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: reducer signatures must match the following pattern:\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: where each `Ti` type implements `SpacetimeType`.\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[0m: required by a bound in `register_reducer`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spacetimedb-1.11.1/src/rt.rs:746:81\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m746\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn register_reducer<'a, A: Args<'a>, I: FnInfo>(_: impl Reducer<'a, A>) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10m^^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10mrequired by this bound in `register_reducer`\u001b[0m\n\n\u001b[0m\u001b[1m\u001b[38;5;9merror[E0277]\u001b[0m\u001b[0m\u001b[1m: invalid reducer signature\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:23:8\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m22\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m#[reducer]\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mrequired by a bound introduced by this call\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m23\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn tick(_ctx: &ReducerContext, _row: TickTimer) {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;9mthis reducer signature is not valid\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mhelp\u001b[0m\u001b[0m: the trait `Reducer<'_, _>` is not implemented for fn item `for<'a> fn(&'a ReducerContext, {type error}) {tick}`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: reducer signatures must match the following pattern:\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `Fn(&ReducerContext, [T1, ...]) [-> Result<(), impl Display>]`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: where each `Ti` type implements `SpacetimeType`.\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: \u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;10mnote\u001b[0m\u001b[0m: required by a bound in `invoke_reducer`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0m/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/spacetimedb-1.11.1/src/rt.rs:45:19\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m44\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mpub fn invoke_reducer<'a, A: Args<'a>>(\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--------------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mrequired by a bound in this function\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m45\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m reducer: impl Reducer<'a, A>,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10m^^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;10mrequired by this bound in `invoke_reducer`\u001b[0m\n\n\u001b[0m\u001b[1mSome errors have detailed explanations: E0277, E0412, E0422, E0599.\u001b[0m\n\u001b[0m\u001b[1mFor more information about an error, try `rustc --explain E0277`.\u001b[0m\n\u001b[1m\u001b[31merror\u001b[0m\u001b[1m:\u001b[0m could not compile `spacetime-module` (lib) due to 8 previous errors\nError: command [\"cargo\", \"build\", \"--config=net.git-fetch-with-cli=true\", \"--target=wasm32-unknown-unknown\", \"--release\", \"--message-format=json-render-diagnostics\"] exited with code 101\n\n--- stdout ---\n",
"phase": "build_or_publish"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.556796888Z",
- "finished_at": "2026-01-16T00:55:36.775006738Z"
+ "started_at": "2026-01-17T02:25:38.991765459Z",
+ "finished_at": "2026-01-17T02:28:17.929994467Z"
},
"t_018_constraints": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -897,7 +902,7 @@
"model_name": "GPT-5",
"total_tests": 3,
"passed_tests": 2,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = accounts, index(name = by_name, btree(columns = [name])))]\npub struct Account {\n #[primary_key]\n id: i32,\n #[unique]\n email: String,\n name: String,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let accounts = ctx.db.accounts();\n let ids: Vec = accounts.iter().map(|a| a.id).collect();\n for id in ids {\n accounts.id().delete(&id);\n }\n accounts.insert(Account {\n id: 1,\n email: \"a@example.com\".to_string(),\n name: \"Alice\".to_string(),\n });\n accounts.insert(Account {\n id: 2,\n email: \"b@example.com\".to_string(),\n name: \"Bob\".to_string(),\n });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = accounts, index(name = by_name, btree(columns = [name])))]\npub struct Account {\n #[primary_key]\n id: i32,\n #[unique]\n email: String,\n name: String,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let _ = ctx.db.accounts().try_insert(Account {\n id: 1,\n email: \"a@example.com\".to_string(),\n name: \"Alice\".to_string(),\n });\n let _ = ctx.db.accounts().try_insert(Account {\n id: 2,\n email: \"b@example.com\".to_string(),\n name: \"Bob\".to_string(),\n });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-018-constraints-golden",
@@ -913,19 +918,11 @@
"llm_db": "schema-t-018-constraints-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
},
- "constraints_row_parity_after_seed": {
- "pass": false,
- "partial": 0.0,
- "notes": {
- "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `accounts`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:39633/v1/database/c2001c7a5adcf4f79a3117c2c3671a576d84bde5f0e9152c5c660a77c5074614/sql)\n",
- "phase": "sql_golden"
- }
- },
"constraints_seed_two_rows": {
"pass": true,
"partial": 1.0,
@@ -934,11 +931,19 @@
"expected": 1,
"sql": "SELECT COUNT(*) AS n FROM accounts WHERE id=2"
}
+ },
+ "constraints_row_parity_after_seed": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `accounts`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:46587/v1/database/c2007d48b169e0048c4dd9c53c8d47e40c7761a8814064fe65c573b1ce844864/sql)\n",
+ "phase": "sql_golden"
+ }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.556487089Z",
- "finished_at": "2026-01-16T00:55:35.376822731Z"
+ "started_at": "2026-01-17T02:25:38.991438731Z",
+ "finished_at": "2026-01-17T02:28:15.400501818Z"
},
"t_019_many_to_many": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -948,7 +953,7 @@
"model_name": "GPT-5",
"total_tests": 5,
"passed_tests": 5,
- "llm_output": "use spacetimedb::{ReducerContext, Table};\n\n#[spacetimedb::table(name = users)]\npub struct User {\n #[primary_key]\n user_id: i32,\n name: String,\n}\n\n#[spacetimedb::table(name = groups)]\npub struct Group {\n #[primary_key]\n group_id: i32,\n title: String,\n}\n\n#[spacetimedb::table(\n name = memberships,\n index(name = by_user, btree(columns = [user_id])),\n index(name = by_group, btree(columns = [group_id]))\n)]\npub struct Membership {\n #[primary_key]\n id: i32,\n user_id: i32,\n group_id: i32,\n}\n\n#[spacetimedb::reducer]\npub fn seed(ctx: &ReducerContext) {\n let mids: Vec = ctx.db.memberships().iter().map(|m| m.id).collect();\n for id in mids {\n ctx.db.memberships().id().delete(&id);\n }\n let uids: Vec = ctx.db.users().iter().map(|u| u.user_id).collect();\n for uid in uids {\n ctx.db.users().user_id().delete(&uid);\n }\n let gids: Vec = ctx.db.groups().iter().map(|g| g.group_id).collect();\n for gid in gids {\n ctx.db.groups().group_id().delete(&gid);\n }\n\n ctx.db.users().insert(User { user_id: 1, name: \"Alice\".to_string() });\n ctx.db.users().insert(User { user_id: 2, name: \"Bob\".to_string() });\n\n ctx.db.groups().insert(Group { group_id: 10, title: \"Admin\".to_string() });\n ctx.db.groups().insert(Group { group_id: 20, title: \"Dev\".to_string() });\n\n ctx.db.memberships().insert(Membership { id: 1, user_id: 1, group_id: 10 });\n ctx.db.memberships().insert(Membership { id: 2, user_id: 1, group_id: 20 });\n ctx.db.memberships().insert(Membership { id: 3, user_id: 2, group_id: 20 });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = users)]\npub struct User {\n #[primary_key]\n user_id: i32,\n name: String,\n}\n\n#[table(name = groups)]\npub struct Group {\n #[primary_key]\n group_id: i32,\n title: String,\n}\n\n#[table(\n name = memberships,\n index(name = by_user, btree(columns = [user_id])),\n index(name = by_group, btree(columns = [group_id]))\n)]\npub struct Membership {\n #[primary_key]\n id: i32,\n user_id: i32,\n group_id: i32,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let users = ctx.db.users();\n users.insert(User { user_id: 1, name: \"Alice\".to_string() });\n users.insert(User { user_id: 2, name: \"Bob\".to_string() });\n\n let groups = ctx.db.groups();\n groups.insert(Group { group_id: 10, title: \"Admin\".to_string() });\n groups.insert(Group { group_id: 20, title: \"Dev\".to_string() });\n\n let memberships = ctx.db.memberships();\n memberships.insert(Membership { id: 1, user_id: 1, group_id: 10 });\n memberships.insert(Membership { id: 2, user_id: 1, group_id: 20 });\n memberships.insert(Membership { id: 3, user_id: 2, group_id: 20 });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-019-many-to-many-golden",
@@ -956,22 +961,22 @@
"work_dir_golden": "target/llm-runs/schema/t_019_many_to_many/rust/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_019_many_to_many/rust/server/gpt-5/llm",
"scorer_details": {
- "m2m_has_1_10": {
+ "m2m_has_1_20": {
"pass": true,
"partial": 1.0,
"notes": {
"actual": 1,
"expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM memberships WHERE user_id=1 AND group_id=10"
+ "sql": "SELECT COUNT(*) AS n FROM memberships WHERE user_id=1 AND group_id=20"
}
},
- "m2m_has_1_20": {
+ "memberships_three_rows": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM memberships WHERE user_id=1 AND group_id=20"
+ "actual": 3,
+ "expected": 3,
+ "sql": "SELECT COUNT(*) AS n FROM memberships"
}
},
"schema_parity": {
@@ -982,33 +987,33 @@
"llm_db": "schema-t-019-many-to-many-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
},
- "m2m_has_2_20": {
+ "m2m_has_1_10": {
"pass": true,
"partial": 1.0,
"notes": {
"actual": 1,
"expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM memberships WHERE user_id=2 AND group_id=20"
+ "sql": "SELECT COUNT(*) AS n FROM memberships WHERE user_id=1 AND group_id=10"
}
},
- "memberships_three_rows": {
+ "m2m_has_2_20": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 3,
- "expected": 3,
- "sql": "SELECT COUNT(*) AS n FROM memberships"
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM memberships WHERE user_id=2 AND group_id=20"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.555877349Z",
- "finished_at": "2026-01-16T00:55:39.920644423Z"
+ "started_at": "2026-01-17T02:25:38.990785156Z",
+ "finished_at": "2026-01-17T02:28:17.183262809Z"
},
"t_020_ecs": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -1018,7 +1023,7 @@
"model_name": "GPT-5",
"total_tests": 5,
"passed_tests": 5,
- "llm_output": "use spacetimedb::{reducer, table, ReducerContext, Table, UniqueColumn};\n\n#[table(name = entities)]\npub struct Entity {\n #[primary_key]\n id: i32,\n}\n\n#[table(name = positions)]\npub struct Position {\n #[primary_key]\n entity_id: i32,\n x: i32,\n y: i32,\n}\n\n#[table(name = velocities)]\npub struct Velocity {\n #[primary_key]\n entity_id: i32,\n vx: i32,\n vy: i32,\n}\n\n#[table(name = next_positions)]\npub struct NextPosition {\n #[primary_key]\n entity_id: i32,\n x: i32,\n y: i32,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let _ = ctx.db.entities().try_insert(Entity { id: 1 });\n let _ = ctx.db.entities().try_insert(Entity { id: 2 });\n\n let p1 = Position {\n entity_id: 1,\n x: 0,\n y: 0,\n };\n if ctx.db.positions().entity_id().find(1).is_some() {\n let _ = ctx.db.positions().entity_id().update(p1);\n } else {\n let _ = ctx.db.positions().insert(p1);\n }\n\n let v1 = Velocity {\n entity_id: 1,\n vx: 1,\n vy: 0,\n };\n if ctx.db.velocities().entity_id().find(1).is_some() {\n let _ = ctx.db.velocities().entity_id().update(v1);\n } else {\n let _ = ctx.db.velocities().insert(v1);\n }\n\n let p2 = Position {\n entity_id: 2,\n x: 10,\n y: 0,\n };\n if ctx.db.positions().entity_id().find(2).is_some() {\n let _ = ctx.db.positions().entity_id().update(p2);\n } else {\n let _ = ctx.db.positions().insert(p2);\n }\n\n let v2 = Velocity {\n entity_id: 2,\n vx: -2,\n vy: 3,\n };\n if ctx.db.velocities().entity_id().find(2).is_some() {\n let _ = ctx.db.velocities().entity_id().update(v2);\n } else {\n let _ = ctx.db.velocities().insert(v2);\n }\n}\n\n#[reducer]\npub fn step(ctx: &ReducerContext) {\n for p in ctx.db.positions().iter() {\n if let Some(v) = ctx.db.velocities().entity_id().find(p.entity_id) {\n let np = NextPosition {\n entity_id: p.entity_id,\n x: p.x + v.vx,\n y: p.y + v.vy,\n };\n if ctx\n .db\n .next_positions()\n .entity_id()\n .find(p.entity_id)\n .is_some()\n {\n let _ = ctx.db.next_positions().entity_id().update(np);\n } else {\n let _ = ctx.db.next_positions().insert(np);\n }\n }\n }\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = entities)]\npub struct Entity {\n #[primary_key]\n id: i32,\n}\n\n#[table(name = positions)]\npub struct Position {\n #[primary_key]\n entity_id: i32,\n x: i32,\n y: i32,\n}\n\n#[table(name = velocities)]\npub struct Velocity {\n #[primary_key]\n entity_id: i32,\n vx: i32,\n vy: i32,\n}\n\n#[table(name = next_positions)]\npub struct NextPosition {\n #[primary_key]\n entity_id: i32,\n x: i32,\n y: i32,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n if ctx.db.entities().id().find(1).is_none() {\n let _ = ctx.db.entities().insert(Entity { id: 1 });\n }\n match ctx.db.positions().entity_id().find(1) {\n Some(mut p) => {\n p.x = 0;\n p.y = 0;\n let _ = ctx.db.positions().entity_id().update(p);\n }\n None => {\n let _ = ctx.db.positions().insert(Position { entity_id: 1, x: 0, y: 0 });\n }\n }\n match ctx.db.velocities().entity_id().find(1) {\n Some(mut v) => {\n v.vx = 1;\n v.vy = 0;\n let _ = ctx.db.velocities().entity_id().update(v);\n }\n None => {\n let _ = ctx.db.velocities().insert(Velocity { entity_id: 1, vx: 1, vy: 0 });\n }\n }\n\n if ctx.db.entities().id().find(2).is_none() {\n let _ = ctx.db.entities().insert(Entity { id: 2 });\n }\n match ctx.db.positions().entity_id().find(2) {\n Some(mut p) => {\n p.x = 10;\n p.y = 0;\n let _ = ctx.db.positions().entity_id().update(p);\n }\n None => {\n let _ = ctx.db.positions().insert(Position { entity_id: 2, x: 10, y: 0 });\n }\n }\n match ctx.db.velocities().entity_id().find(2) {\n Some(mut v) => {\n v.vx = -2;\n v.vy = 3;\n let _ = ctx.db.velocities().entity_id().update(v);\n }\n None => {\n let _ = ctx.db.velocities().insert(Velocity { entity_id: 2, vx: -2, vy: 3 });\n }\n }\n}\n\n#[reducer]\npub fn step(ctx: &ReducerContext) {\n for pos in ctx.db.positions().iter() {\n if let Some(vel) = ctx.db.velocities().entity_id().find(pos.entity_id) {\n let nx = pos.x + vel.vx;\n let ny = pos.y + vel.vy;\n match ctx.db.next_positions().entity_id().find(pos.entity_id) {\n Some(mut np) => {\n np.x = nx;\n np.y = ny;\n let _ = ctx.db.next_positions().entity_id().update(np);\n }\n None => {\n let _ = ctx.db.next_positions().insert(NextPosition {\n entity_id: pos.entity_id,\n x: nx,\n y: ny,\n });\n }\n }\n }\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-020-ecs-golden",
@@ -1044,13 +1049,13 @@
"sql": "SELECT COUNT(*) AS n FROM next_positions WHERE entity_id=1 AND x=1 AND y=0"
}
},
- "ecs_step_next_positions_count": {
+ "ecs_next_pos_entity2": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 2,
- "expected": 2,
- "sql": "SELECT COUNT(*) AS n FROM next_positions"
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM next_positions WHERE entity_id=2 AND x=8 AND y=3"
}
},
"schema_parity": {
@@ -1061,24 +1066,24 @@
"llm_db": "schema-t-020-ecs-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:39633",
+ "server": "http://127.0.0.1:46587",
"tables_diff": null,
"tables_equal": true
}
},
- "ecs_next_pos_entity2": {
+ "ecs_step_next_positions_count": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM next_positions WHERE entity_id=2 AND x=8 AND y=3"
+ "actual": 2,
+ "expected": 2,
+ "sql": "SELECT COUNT(*) AS n FROM next_positions"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.556208899Z",
- "finished_at": "2026-01-16T00:55:40.201729445Z"
+ "started_at": "2026-01-17T02:25:38.991130819Z",
+ "finished_at": "2026-01-17T02:28:20.539029094Z"
},
"t_021_multi_column_index": {
"hash": "40baf0d53a9de9eab21250fad1d96f530213fd7dd09cb9a3d97f40ff548f0d8a",
@@ -1088,7 +1093,7 @@
"model_name": "GPT-5",
"total_tests": 4,
"passed_tests": 4,
- "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = logs, index(name = by_user_day, btree(columns = [user_id, day])))]\npub struct Log {\n #[primary_key]\n id: i32,\n user_id: i32,\n day: i32,\n message: String,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let logs = ctx.db.logs();\n\n let existing_ids: Vec = logs.iter().map(|r| r.id).collect();\n for id in existing_ids {\n logs.id().delete(&id);\n }\n\n logs.insert(Log { id: 1, user_id: 7, day: 1, message: \"a\".to_string() });\n logs.insert(Log { id: 2, user_id: 7, day: 2, message: \"b\".to_string() });\n logs.insert(Log { id: 3, user_id: 9, day: 1, message: \"c\".to_string() });\n}",
+ "llm_output": "use spacetimedb::{table, reducer, ReducerContext, Table};\n\n#[table(name = logs, index(name = by_user_day, btree(columns = [user_id, day])))]\npub struct Log {\n #[primary_key]\n id: i32,\n user_id: i32,\n day: i32,\n message: String,\n}\n\n#[reducer]\npub fn seed(ctx: &ReducerContext) {\n let tbl = ctx.db.logs();\n let ids: Vec = tbl.iter().map(|r| r.id).collect();\n for id in ids {\n tbl.id().delete(&id);\n }\n\n tbl.insert(Log { id: 1, user_id: 7, day: 1, message: \"a\".to_string() });\n tbl.insert(Log { id: 2, user_id: 7, day: 2, message: \"b\".to_string() });\n tbl.insert(Log { id: 3, user_id: 9, day: 1, message: \"c\".to_string() });\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-021-multi-column-index-golden",
@@ -1096,50 +1101,50 @@
"work_dir_golden": "target/llm-runs/schema/t_021_multi_column_index/rust/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_021_multi_column_index/rust/server/gpt-5/llm",
"scorer_details": {
- "mcindex_lookup_u7_d2": {
+ "schema_parity": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM logs WHERE user_id=7 AND day=2"
+ "golden_db": "schema-t-021-multi-column-index-golden",
+ "llm_db": "schema-t-021-multi-column-index-gpt-5-llm",
+ "reducers_diff": null,
+ "reducers_equal": true,
+ "server": "http://127.0.0.1:46587",
+ "tables_diff": null,
+ "tables_equal": true
}
},
- "mcindex_seed_count": {
+ "mcindex_lookup_u7_d1": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 3,
- "expected": 3,
- "sql": "SELECT COUNT(*) AS n FROM logs"
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM logs WHERE user_id=7 AND day=1"
}
},
- "schema_parity": {
+ "mcindex_lookup_u7_d2": {
"pass": true,
"partial": 1.0,
"notes": {
- "golden_db": "schema-t-021-multi-column-index-golden",
- "llm_db": "schema-t-021-multi-column-index-gpt-5-llm",
- "reducers_diff": null,
- "reducers_equal": true,
- "server": "http://127.0.0.1:39633",
- "tables_diff": null,
- "tables_equal": true
+ "actual": 1,
+ "expected": 1,
+ "sql": "SELECT COUNT(*) AS n FROM logs WHERE user_id=7 AND day=2"
}
},
- "mcindex_lookup_u7_d1": {
+ "mcindex_seed_count": {
"pass": true,
"partial": 1.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM logs WHERE user_id=7 AND day=1"
+ "actual": 3,
+ "expected": 3,
+ "sql": "SELECT COUNT(*) AS n FROM logs"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T00:53:05.557086109Z",
- "finished_at": "2026-01-16T00:55:40.398017385Z"
+ "started_at": "2026-01-17T02:25:38.992084803Z",
+ "finished_at": "2026-01-17T02:28:17.745571962Z"
}
}
}
@@ -1330,14 +1335,14 @@
"modes": [
{
"mode": "docs",
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"models": [
{
"name": "GPT-5",
"route_api_model": "gpt-5",
"tasks": {
"t_000_empty_reducers": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_000_empty_reducers",
"lang": "csharp",
"golden_published": true,
@@ -1360,25 +1365,25 @@
"llm_db": "basics-t-000-empty-reducers-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:08:18.856841039Z",
- "finished_at": "2026-01-16T01:08:44.106092534Z"
+ "started_at": "2026-01-17T02:40:24.282055723Z",
+ "finished_at": "2026-01-17T02:40:56.269475914Z"
},
"t_001_basic_tables": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_001_basic_tables",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 1,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n}\n\n[SpacetimeDB.Table(Name = \"users\", Public = true)]\npublic partial struct Users\n{\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n}\n\n[SpacetimeDB.Table(Name = \"products\", Public = true)]\npublic partial struct Products\n{\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Title;\n public float Price;\n public bool InStock;\n}\n\n[SpacetimeDB.Table(Name = \"notes\", Public = true)]\npublic partial struct Notes\n{\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Body;\n public long Rating;\n public bool Pinned;\n}",
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Table]\n public partial struct Product\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Title;\n public float Price;\n public bool InStock;\n }\n\n [SpacetimeDB.Table]\n public partial struct Note\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Body;\n public long Rating;\n public bool Pinned;\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-001-basic-tables-golden",
@@ -1394,25 +1399,25 @@
"llm_db": "basics-t-001-basic-tables-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:10:46.550782288Z",
- "finished_at": "2026-01-16T01:11:21.093863299Z"
+ "started_at": "2026-01-17T02:43:28.640515207Z",
+ "finished_at": "2026-01-17T02:43:58.187104574Z"
},
"t_002_scheduled_table": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_002_scheduled_table",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 1,
- "llm_output": "using System;\nusing SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"tick_timer\", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]\n public partial struct TickTimer\n {\n [PrimaryKey, AutoInc]\n public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [Reducer]\n public static void Tick(ReducerContext ctx, TickTimer timer)\n {\n }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.tick_timer.Insert(new TickTimer\n {\n ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromMilliseconds(50))\n });\n }\n}",
+ "llm_output": "using System;\nusing SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"TickTimer\", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]\n public partial struct TickTimer\n {\n [PrimaryKey, AutoInc]\n public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.TickTimer.Insert(new TickTimer\n {\n ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromMilliseconds(50))\n });\n }\n\n [Reducer]\n public static void Tick(ReducerContext ctx, TickTimer timer)\n {\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-002-scheduled-table-golden",
@@ -1428,25 +1433,25 @@
"llm_db": "basics-t-002-scheduled-table-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:09:55.247537588Z",
- "finished_at": "2026-01-16T01:10:36.710734521Z"
+ "started_at": "2026-01-17T02:42:25.159472748Z",
+ "finished_at": "2026-01-17T02:43:07.893721504Z"
},
"t_003_struct_in_table": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_003_struct_in_table",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 1,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Table(Name = \"entities\")]\n public partial struct Entity\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Position Pos;\n }\n}",
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Table(Name = \"Entity\", Public = true)]\n public partial struct Entity\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Position Pos;\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-003-struct-in-table-golden",
@@ -1462,25 +1467,25 @@
"llm_db": "basics-t-003-struct-in-table-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:10:36.710775076Z",
- "finished_at": "2026-01-16T01:11:05.707794534Z"
+ "started_at": "2026-01-17T02:43:07.893755608Z",
+ "finished_at": "2026-01-17T02:43:39.643821282Z"
},
"t_004_insert": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_004_insert",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 2,
- "passed_tests": 2,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"users\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void InsertUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n ctx.Db.users.Insert(new User\n {\n Id = id,\n Name = name,\n Age = age,\n Active = active\n });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void InsertUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n ctx.Db.User.Insert(new User\n {\n Id = id,\n Name = name,\n Age = age,\n Active = active\n });\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-004-insert-golden",
@@ -1489,22 +1494,11 @@
"work_dir_llm": "target/llm-runs/basics/t_004_insert/csharp/server/gpt-5/llm",
"scorer_details": {
"data_parity_insert_user": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "args": [
- 1,
- "Alice",
- 30,
- true
- ],
- "golden_db": "basics-t-004-insert-golden",
- "golden_out": "Id | Name | Age | Active ----+---------+-----+-------- 1 | \"Alice\" | 30 | true",
- "llm_db": "basics-t-004-insert-gpt-5-llm",
- "llm_out": "Id | Name | Age | Active ----+---------+-----+-------- 1 | \"Alice\" | 30 | true",
- "query": "SELECT Id, Name, Age, Active FROM users WHERE Id=1",
- "reducer": "InsertUser",
- "server": "http://127.0.0.1:37321"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200933266559154a4d1e8adf441af2b283882c3206ed34f5c4ef907d29de6a7/sql)\n",
+ "phase": "sql_golden"
}
},
"schema_parity": {
@@ -1515,25 +1509,25 @@
"llm_db": "basics-t-004-insert-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:11:21.093906981Z",
- "finished_at": "2026-01-16T01:11:54.905124723Z"
+ "started_at": "2026-01-17T02:43:58.187152683Z",
+ "finished_at": "2026-01-17T02:44:26.839511728Z"
},
"t_005_update": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_005_update",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"users\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void UpdateUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n var existing = ctx.Db.users.Id.Find(id);\n if (existing != null)\n {\n var u = existing.Value;\n u.Name = name;\n u.Age = age;\n u.Active = active;\n ctx.Db.users.Id.Update(u);\n }\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void UpdateUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n if (ctx.Db.User.Id.Find(id) is User user)\n {\n user.Id = id;\n user.Name = name;\n user.Age = age;\n user.Active = active;\n ctx.Db.User.Id.Update(user);\n }\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-005-update-golden",
@@ -1541,6 +1535,14 @@
"work_dir_golden": "target/llm-runs/basics/t_005_update/csharp/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_005_update/csharp/server/gpt-5/llm",
"scorer_details": {
+ "data_parity_update_user": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime call failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: Response text: SpacetimeDB.NoSuchRowException: The row was not found, e.g., in an update call\n at SpacetimeDB.Internal.FFI.CheckedStatus.Marshaller.ConvertToManaged(Errno )\n at SpacetimeDB.Internal.FFI.datastore_update_bsatn(TableId , IndexId , Span`1 , UInt32& )\n at SpacetimeDB.Internal.UniqueIndex`4[[SpacetimeDB.Internal.TableHandles.User, StdbModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Module.User, StdbModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[SpacetimeDB.BSATN.I32, SpacetimeDB.BSATN.Runtime, Version=1.6.0.0, Culture=neutral, PublicKeyToken=null]].DoUpdate(User )\n at SpacetimeDB.Internal.TableHandles.User.IdUniqueIndex.Update(User )\n at Module.UpdateUser(ReducerContext , Int32 , String , Int32 , Boolean )\n at ModuleRegistration.UpdateUser.Invoke(BinaryReader , IReducerContext )\n at SpacetimeDB.Internal.Module.__call_reducer__(UInt32 id, UInt64 sender_0, UInt64 sender_1, UInt64 sender_2, UInt64 sender_3, UInt64 conn_id_0, UInt64 conn_id_1, Timestamp timestamp, BytesSource args, BytesSink error)\n\nCaused by:\n HTTP status server error (530 ) for url (http://127.0.0.1:35677/v1/database/c200523e08ccb74ff8d13ab66f492b457bb8aeee5e4fdafc5d1fec7431a9b4e9/call/UpdateUser)\n",
+ "phase": "call_reducer_golden"
+ }
+ },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -1549,51 +1551,34 @@
"llm_db": "basics-t-005-update-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
},
"seed_users_row": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200523e08ccb74ff8d13ab66f492b457bb8aeee5e4fdafc5d1fec7431a9b4e9/sql)\n",
+ "phase": "sql_golden",
"sql": "INSERT INTO users(Id, Name, Age, Active) VALUES (1, 'Alice', 30, true)"
}
- },
- "data_parity_update_user": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "args": [
- 1,
- "Alice2",
- 31,
- false
- ],
- "golden_db": "basics-t-005-update-golden",
- "golden_out": "Id | Name | Age | Active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
- "llm_db": "basics-t-005-update-gpt-5-llm",
- "llm_out": "Id | Name | Age | Active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
- "query": "SELECT Id, Name, Age, Active FROM users WHERE Id=1",
- "reducer": "UpdateUser",
- "server": "http://127.0.0.1:37321"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:09:35.751798751Z",
- "finished_at": "2026-01-16T01:10:09.499012619Z"
+ "started_at": "2026-01-17T02:42:19.102421878Z",
+ "finished_at": "2026-01-17T02:42:52.237316726Z"
},
"t_006_delete": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_006_delete",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"users\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void DeleteUser(ReducerContext ctx, int id)\n {\n ctx.Db.users.Id.Delete(id);\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\")]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void DeleteUser(ReducerContext ctx, int id)\n {\n ctx.Db.User.Id.Delete(id);\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-006-delete-golden",
@@ -1601,22 +1586,6 @@
"work_dir_golden": "target/llm-runs/basics/t_006_delete/csharp/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_006_delete/csharp/server/gpt-5/llm",
"scorer_details": {
- "delete_user_count_zero": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 0,
- "expected": 0,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE Id=1"
- }
- },
- "seed_users_row": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "sql": "INSERT INTO users(Id, Name, Age, Active) VALUES (1, 'Alice', 30, true)"
- }
- },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -1625,25 +1594,42 @@
"llm_db": "basics-t-006-delete-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
+ },
+ "delete_user_count_zero": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2004b74d3be00407c2d02282ee08b25e5e8242063995dea613d8fc1467ef09e/sql)\n",
+ "phase": "sql"
+ }
+ },
+ "seed_users_row": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200052ec3b2af2555b2766443d690ff70a4d07ffd410be917b433381552b936/sql)\n",
+ "phase": "sql_golden",
+ "sql": "INSERT INTO users(Id, Name, Age, Active) VALUES (1, 'Alice', 30, true)"
+ }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:08:44.106133480Z",
- "finished_at": "2026-01-16T01:09:18.793710872Z"
+ "started_at": "2026-01-17T02:40:56.269507302Z",
+ "finished_at": "2026-01-17T02:41:28.991775660Z"
},
"t_007_crud": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_007_crud",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 4,
- "passed_tests": 4,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void Crud(ReducerContext ctx)\n {\n ctx.Db.users.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.users.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n\n var user1 = ctx.Db.users.Id.Find(1);\n if (user1 is User u1)\n {\n u1.Name = \"Alice2\";\n u1.Age = 31;\n u1.Active = false;\n ctx.Db.users.Id.Update(u1);\n }\n\n ctx.Db.users.Id.Delete(2);\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer]\n public static void Crud(ReducerContext ctx)\n {\n ctx.Db.User.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.User.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n\n if (ctx.Db.User.Id.Find(1) is User user)\n {\n user.Name = \"Alice2\";\n user.Age = 31;\n user.Active = false;\n ctx.Db.User.Id.Update(user);\n }\n\n ctx.Db.User.Id.Delete(2);\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-007-crud-golden",
@@ -1651,36 +1637,20 @@
"work_dir_golden": "target/llm-runs/basics/t_007_crud/csharp/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_007_crud/csharp/server/gpt-5/llm",
"scorer_details": {
- "crud_row_id2_deleted": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 0,
- "expected": 0,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE Id=2"
- }
- },
- "crud_total_count_one": {
- "pass": true,
- "partial": 1.0,
+ "crud_row_id1_parity": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM users"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2006b22042e3fba4f36b1b44b80a07da826122ccc12419c53c511830a0c78f9/sql)\n",
+ "phase": "sql_golden"
}
},
- "crud_row_id1_parity": {
- "pass": true,
- "partial": 1.0,
+ "crud_row_id2_deleted": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "args": [],
- "golden_db": "basics-t-007-crud-golden",
- "golden_out": "Id | Name | Age | Active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
- "llm_db": "basics-t-007-crud-gpt-5-llm",
- "llm_out": "Id | Name | Age | Active ----+----------+-----+-------- 1 | \"Alice2\" | 31 | false",
- "query": "SELECT Id, Name, Age, Active FROM users WHERE Id=1",
- "reducer": "Crud",
- "server": "http://127.0.0.1:37321"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2003c5aaa4b9c829e64c505f35cb7241f299a733d0f5a89da64241c5e052096/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -1691,25 +1661,33 @@
"llm_db": "basics-t-007-crud-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
+ },
+ "crud_total_count_one": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2003c5aaa4b9c829e64c505f35cb7241f299a733d0f5a89da64241c5e052096/sql)\n",
+ "phase": "sql"
+ }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:08:24.082507036Z",
- "finished_at": "2026-01-16T01:09:02.249009409Z"
+ "started_at": "2026-01-17T02:40:51.462439194Z",
+ "finished_at": "2026-01-17T02:41:35.805469514Z"
},
"t_008_index_lookup": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_008_index_lookup",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"users\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Table(Name = \"results\", Public = true)]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void LookupUserName(ReducerContext ctx, int id)\n {\n if (ctx.Db.users.Id.Find(id) is User user)\n {\n ctx.Db.results.Insert(new Result\n {\n Id = user.Id,\n Name = user.Name\n });\n }\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Table(Name = \"Result\", Public = true)]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void LookupUserName(ReducerContext ctx, int id)\n {\n if (ctx.Db.User.Id.Find(id) is User user)\n {\n ctx.Db.Result.Insert(new Result\n {\n Id = user.Id,\n Name = user.Name\n });\n }\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-008-index-lookup-golden",
@@ -1717,22 +1695,6 @@
"work_dir_golden": "target/llm-runs/basics/t_008_index_lookup/csharp/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_008_index_lookup/csharp/server/gpt-5/llm",
"scorer_details": {
- "index_lookup_projection_parity": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "args": [
- 1
- ],
- "golden_db": "basics-t-008-index-lookup-golden",
- "golden_out": "Id | Name ----+--------- 1 | \"Alice\"",
- "llm_db": "basics-t-008-index-lookup-gpt-5-llm",
- "llm_out": "Id | Name ----+--------- 1 | \"Alice\"",
- "query": "SELECT Id, Name FROM results WHERE Id=1",
- "reducer": "LookupUserName",
- "server": "http://127.0.0.1:37321"
- }
- },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -1741,32 +1703,42 @@
"llm_db": "basics-t-008-index-lookup-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
},
"seed_user_row": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2009fb018392f6be9608948e77d6c1cbdd60e04fc795fc595009cb76a175394/sql)\n",
+ "phase": "sql_golden",
"sql": "INSERT INTO users(Id, Name, Age, Active) VALUES (1, 'Alice', 30, true)"
}
+ },
+ "index_lookup_projection_parity": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2009fb018392f6be9608948e77d6c1cbdd60e04fc795fc595009cb76a175394/sql)\n",
+ "phase": "sql_golden"
+ }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:09:02.249052590Z",
- "finished_at": "2026-01-16T01:09:35.751755801Z"
+ "started_at": "2026-01-17T02:41:28.991836824Z",
+ "finished_at": "2026-01-17T02:42:19.102358640Z"
},
"t_009_init": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_009_init",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 4,
- "passed_tests": 4,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"users\")]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.users.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.users.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\", Public = true)]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [SpacetimeDB.Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.User.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.User.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-009-init-golden",
@@ -1774,22 +1746,12 @@
"work_dir_golden": "target/llm-runs/basics/t_009_init/csharp/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_009_init/csharp/server/gpt-5/llm",
"scorer_details": {
- "init_seed_alice": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE Id=1 AND Name='Alice' AND Age=30 AND Active=true"
- }
- },
"init_seed_bob": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM users WHERE Id=2 AND Name='Bob' AND Age=22 AND Active=false"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20058e57001dda166048aa4be9fd4471b9b37f134fd7481e1ada013429df130/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -1800,34 +1762,41 @@
"llm_db": "basics-t-009-init-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
},
+ "init_seed_alice": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20058e57001dda166048aa4be9fd4471b9b37f134fd7481e1ada013429df130/sql)\n",
+ "phase": "sql"
+ }
+ },
"init_total_two": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 2,
- "expected": 2,
- "sql": "SELECT COUNT(*) AS n FROM users"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `users`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20058e57001dda166048aa4be9fd4471b9b37f134fd7481e1ada013429df130/sql)\n",
+ "phase": "sql"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:09:18.793757880Z",
- "finished_at": "2026-01-16T01:09:55.247472877Z"
+ "started_at": "2026-01-17T02:41:35.805507675Z",
+ "finished_at": "2026-01-17T02:42:25.159425259Z"
},
"t_010_connect": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_010_connect",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 1,
"passed_tests": 1,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"events\")]\n public partial struct Event\n {\n [PrimaryKey, AutoInc]\n public int Id;\n public string Kind;\n }\n\n [Reducer(ReducerKind.ClientConnected)]\n public static void ClientConnected(ReducerContext ctx)\n {\n ctx.Db.events.Insert(new Event\n {\n Kind = \"connected\"\n });\n }\n\n [Reducer(ReducerKind.ClientDisconnected)]\n public static void ClientDisconnected(ReducerContext ctx)\n {\n ctx.Db.events.Insert(new Event\n {\n Kind = \"disconnected\"\n });\n }\n}",
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Event\")]\n public partial struct Event\n {\n [SpacetimeDB.PrimaryKey]\n [SpacetimeDB.AutoInc]\n public int Id;\n public string Kind;\n }\n\n [Reducer(ReducerKind.ClientConnected)]\n public static void ClientConnected(ReducerContext ctx)\n {\n ctx.Db.Event.Insert(new Event\n {\n Id = 0,\n Kind = \"connected\",\n });\n }\n\n [Reducer(ReducerKind.ClientDisconnected)]\n public static void ClientDisconnected(ReducerContext ctx)\n {\n ctx.Db.Event.Insert(new Event\n {\n Id = 0,\n Kind = \"disconnected\",\n });\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-010-connect-golden",
@@ -1843,25 +1812,25 @@
"llm_db": "basics-t-010-connect-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:11:05.707824440Z",
- "finished_at": "2026-01-16T01:11:41.874533069Z"
+ "started_at": "2026-01-17T02:43:39.643859323Z",
+ "finished_at": "2026-01-17T02:44:14.452325815Z"
},
"t_011_helper_function": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_011_helper_function",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"results\", Public = true)]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int Sum;\n }\n\n public static int Add(int a, int b)\n {\n return a + b;\n }\n\n [SpacetimeDB.Reducer]\n public static void ComputeSum(ReducerContext ctx, int id, int a, int b)\n {\n var sum = Add(a, b);\n ctx.Db.results.Insert(new Result\n {\n Id = id,\n Sum = sum\n });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Result\", Public = true)]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int Sum;\n }\n\n public static int Add(int a, int b)\n {\n return a + b;\n }\n\n [SpacetimeDB.Reducer]\n public static void ComputeSum(ReducerContext ctx, int id, int a, int b)\n {\n ctx.Db.Result.Insert(new Result\n {\n Id = id,\n Sum = Add(a, b)\n });\n }\n}",
"category": "basics",
"route_api_model": "gpt-5",
"golden_db": "basics-t-011-helper-function-golden",
@@ -1869,22 +1838,12 @@
"work_dir_golden": "target/llm-runs/basics/t_011_helper_function/csharp/server/golden",
"work_dir_llm": "target/llm-runs/basics/t_011_helper_function/csharp/server/gpt-5/llm",
"scorer_details": {
- "helper_func_sum_parity": {
- "pass": true,
- "partial": 1.0,
+ "helper_func_sum_abs": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "args": [
- 1,
- 2,
- 3
- ],
- "golden_db": "basics-t-011-helper-function-golden",
- "golden_out": "Id | Sum ----+----- 1 | 5",
- "llm_db": "basics-t-011-helper-function-gpt-5-llm",
- "llm_out": "Id | Sum ----+----- 1 | 5",
- "query": "SELECT Id, Sum FROM results WHERE Id=1",
- "reducer": "ComputeSum",
- "server": "http://127.0.0.1:37321"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200bacfa9e399c2f18f42a36dcfe8479e933d44e6e749cbcc699afdf2cd855d/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -1895,34 +1854,33 @@
"llm_db": "basics-t-011-helper-function-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
},
- "helper_func_sum_abs": {
- "pass": true,
- "partial": 1.0,
+ "helper_func_sum_parity": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM results WHERE Id=1 AND Sum=5"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200b67e605283bc59b71366b2f40ab582a2bbbfc946e2c8510ce84f55424a14/sql)\n",
+ "phase": "sql_golden"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:10:09.499058365Z",
- "finished_at": "2026-01-16T01:10:46.550746821Z"
+ "started_at": "2026-01-17T02:42:52.237342945Z",
+ "finished_at": "2026-01-17T02:43:28.640296252Z"
},
"t_012_spacetime_product_type": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_012_spacetime_product_type",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\n[Type]\npublic partial struct Score\n{\n public int Left;\n public int Right;\n}\n\npublic static partial class Module\n{\n [Table(Name = \"results\")]\n public partial struct Result\n {\n [PrimaryKey]\n public int Id;\n public Score Value;\n }\n\n [Reducer]\n public static void SetScore(ReducerContext ctx, int id, int left, int right)\n {\n ctx.Db.results.Insert(new Result\n {\n Id = id,\n Value = new Score\n {\n Left = left,\n Right = right\n }\n });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Score\n {\n public int Left;\n public int Right;\n }\n\n [SpacetimeDB.Table(Name = \"Result\", Public = true)]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Score Value;\n }\n\n [SpacetimeDB.Reducer]\n public static void SetScore(ReducerContext ctx, int id, int left, int right)\n {\n ctx.Db.Result.Insert(new Result\n {\n Id = id,\n Value = new Score { Left = left, Right = right }\n });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-012-spacetime-product-type-golden",
@@ -1930,60 +1888,49 @@
"work_dir_golden": "target/llm-runs/schema/t_012_spacetime_product_type/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_012_spacetime_product_type/csharp/server/gpt-5/llm",
"scorer_details": {
- "product_type_row_parity": {
+ "schema_parity": {
"pass": true,
"partial": 1.0,
"notes": {
- "args": [
- 1,
- 2,
- 3
- ],
"golden_db": "schema-t-012-spacetime-product-type-golden",
- "golden_out": "Id | Value ----+----------------------- 1 | (Left = 2, Right = 3)",
"llm_db": "schema-t-012-spacetime-product-type-gpt-5-llm",
- "llm_out": "Id | Value ----+----------------------- 1 | (Left = 2, Right = 3)",
- "query": "SELECT Id, Value FROM results WHERE Id=1",
- "reducer": "SetScore",
- "server": "http://127.0.0.1:37321"
+ "reducers_diff": null,
+ "reducers_equal": true,
+ "server": "http://127.0.0.1:35677",
+ "tables_diff": null,
+ "tables_equal": true
}
},
- "product_type_row_count": {
- "pass": true,
- "partial": 1.0,
+ "product_type_row_parity": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM results WHERE Id=1"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200000593a1c1f41d9a7ca9ff85b3f10b7eb54ba57159f084fd8b1d4196e453/sql)\n",
+ "phase": "sql_golden"
}
},
- "schema_parity": {
- "pass": true,
- "partial": 1.0,
+ "product_type_row_count": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "golden_db": "schema-t-012-spacetime-product-type-golden",
- "llm_db": "schema-t-012-spacetime-product-type-gpt-5-llm",
- "reducers_diff": null,
- "reducers_equal": true,
- "server": "http://127.0.0.1:37321",
- "tables_diff": null,
- "tables_equal": true
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200773151fae81e0c0863ae9d0a4534b2934c3b4023363ffb2b7c1930309b46/sql)\n",
+ "phase": "sql"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:06:05.088532998Z",
- "finished_at": "2026-01-16T01:06:46.603812945Z"
+ "started_at": "2026-01-17T02:38:56.019269384Z",
+ "finished_at": "2026-01-17T02:39:27.502611490Z"
},
"t_013_spacetime_sum_type": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_013_spacetime_sum_type",
"lang": "csharp",
- "golden_published": false,
+ "golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 0,
- "llm_output": "using SpacetimeDB;\nusing SpacetimeDB.Types;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Circle\n {\n public int Radius;\n }\n\n [SpacetimeDB.Type]\n public partial struct Rectangle\n {\n public int Width;\n public int Height;\n }\n\n [SpacetimeDB.Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)>;\n\n [SpacetimeDB.Table(Name = \"results\")]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Shape Value;\n }\n\n [SpacetimeDB.Reducer]\n public static void SetCircle(ReducerContext ctx, int id, int radius)\n {\n ctx.Db.results.Insert(new Result\n {\n Id = id,\n Value = new Shape.Circle(new Circle { Radius = radius })\n });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Circle\n {\n public int Radius;\n }\n\n [SpacetimeDB.Type]\n public partial struct Rectangle\n {\n public int Width;\n public int Height;\n }\n\n [SpacetimeDB.Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }\n\n [SpacetimeDB.Table(Name = \"Result\")]\n public partial struct Result\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Shape Value;\n }\n\n [SpacetimeDB.Reducer]\n public static void SetCircle(SpacetimeDB.ReducerContext ctx, int id, int radius)\n {\n var circle = new Circle { Radius = radius };\n var shape = new Shape.Circle(circle);\n ctx.Db.Result.Insert(new Result { Id = id, Value = shape });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-013-spacetime-sum-type-golden",
@@ -1991,28 +1938,49 @@
"work_dir_golden": "target/llm-runs/schema/t_013_spacetime_sum_type/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_013_spacetime_sum_type/csharp/server/gpt-5/llm",
"scorer_details": {
- "publish_error": {
+ "sum_type_row_count": {
"pass": false,
"partial": 0.0,
"notes": {
- "error": "spacetime build (csharp) failed (exit=1)\n--- stderr ---\nError: command [\"dotnet\", \"publish\", \"-c\", \"Release\", \"-v\", \"quiet\"] exited with code 1\n\n--- stdout ---\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_013_spacetime_sum_type/csharp/server/gpt-5/llm/Lib.cs(3,19): error CS0234: The type or namespace name 'Types' does not exist in the namespace 'SpacetimeDB' (are you missing an assembly reference?) [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_013_spacetime_sum_type/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_013_spacetime_sum_type/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/FFI.cs(27,32): warning CS8981: The type name 'results' only contains lower-cased ascii characters. Such names may become reserved for the language. [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_013_spacetime_sum_type/csharp/server/gpt-5/llm/StdbModule.csproj]\n",
- "phase": "build_or_publish"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2009a86be1f0f78642d2ade147db198738ef88aa551d11bb76e42c1c8900e9a/sql)\n",
+ "phase": "sql"
}
- }
- },
- "vendor": "openai",
- "started_at": "2026-01-16T01:07:27.375289930Z",
- "finished_at": "2026-01-16T01:08:18.856814059Z"
- },
- "t_014_elementary_columns": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
- "task": "t_014_elementary_columns",
+ },
+ "schema_parity": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "golden_db": "schema-t-013-spacetime-sum-type-golden",
+ "llm_db": "schema-t-013-spacetime-sum-type-gpt-5-llm",
+ "reducers_diff": null,
+ "reducers_equal": true,
+ "server": "http://127.0.0.1:35677",
+ "tables_diff": null,
+ "tables_equal": true
+ }
+ },
+ "sum_type_row_parity": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `results`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200d82761d2c72dc9a864fd786af31b4c9ef43bb79b633450e8e561c3eea2ce/sql)\n",
+ "phase": "sql_golden"
+ }
+ }
+ },
+ "vendor": "openai",
+ "started_at": "2026-01-17T02:40:02.303793183Z",
+ "finished_at": "2026-01-17T02:40:51.462059047Z"
+ },
+ "t_014_elementary_columns": {
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
+ "task": "t_014_elementary_columns",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"primitives\")]\n public partial struct Primitive\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int Count;\n public long Total;\n public float Price;\n public double Ratio;\n public bool Active;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n var row = new Primitive\n {\n Id = 1,\n Count = 2,\n Total = 3000000000L,\n Price = 1.5f,\n Ratio = 2.25,\n Active = true,\n Name = \"Alice\"\n };\n\n var existing = ctx.Db.primitives.Id.Find(1);\n if (existing is null)\n {\n ctx.Db.primitives.Insert(row);\n }\n else\n {\n ctx.Db.primitives.Id.Update(row);\n }\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Primitive\", Public = true)]\n public partial struct Primitive\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int Count;\n public long Total;\n public float Price;\n public double Ratio;\n public bool Active;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Primitive.Insert(new Primitive\n {\n Id = 1,\n Count = 2,\n Total = 3000000000L,\n Price = 1.5f,\n Ratio = 2.25,\n Active = true,\n Name = \"Alice\"\n });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-014-elementary-columns-golden",
@@ -2021,17 +1989,19 @@
"work_dir_llm": "target/llm-runs/schema/t_014_elementary_columns/csharp/server/gpt-5/llm",
"scorer_details": {
"elementary_columns_row_parity": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "args": [],
- "golden_db": "schema-t-014-elementary-columns-golden",
- "golden_out": "Id | Count | Total | Price | Ratio | Active | Name ----+-------+------------+-------+-------+--------+--------- 1 | 2 | 3000000000 | 1.5 | 2.25 | true | \"Alice\"",
- "llm_db": "schema-t-014-elementary-columns-gpt-5-llm",
- "llm_out": "Id | Count | Total | Price | Ratio | Active | Name ----+-------+------------+-------+-------+--------+--------- 1 | 2 | 3000000000 | 1.5 | 2.25 | true | \"Alice\"",
- "query": "SELECT Id, Count, Total, Price, Ratio, Active, Name FROM primitives WHERE Id=1",
- "reducer": "Seed",
- "server": "http://127.0.0.1:37321"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `primitives`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2003f9c20afc908b6f3fae929b8555ec0e90cf407b107f224de84971189e7b3/sql)\n",
+ "phase": "sql_golden"
+ }
+ },
+ "elementary_columns_row_count": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `primitives`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2009b868c8501f40d75f3046c80b4fa8bc7018d8baf65ff12c991bbab6d605a/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -2042,34 +2012,25 @@
"llm_db": "schema-t-014-elementary-columns-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
- },
- "elementary_columns_row_count": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM primitives WHERE Id=1"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:05:40.135748267Z",
- "finished_at": "2026-01-16T01:07:03.394849892Z"
+ "started_at": "2026-01-17T02:38:36.670677504Z",
+ "finished_at": "2026-01-17T02:39:15.092747325Z"
},
"t_015_product_type_columns": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_015_product_type_columns",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\n[SpacetimeDB.Type]\npublic partial struct Address\n{\n public string Street;\n public int Zip;\n}\n\n[SpacetimeDB.Type]\npublic partial struct Position\n{\n public int X;\n public int Y;\n}\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"profiles\")]\n public partial struct Profile\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Address Home;\n public Address Work;\n public Position Pos;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n var home = new Address { Street = \"1 Main\", Zip = 11111 };\n var work = new Address { Street = \"2 Broad\", Zip = 22222 };\n var pos = new Position { X = 7, Y = 9 };\n ctx.Db.profiles.Insert(new Profile\n {\n Id = 1,\n Home = home,\n Work = work,\n Pos = pos\n });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Address\n {\n public string Street;\n public int Zip;\n }\n\n [SpacetimeDB.Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Table(Name = \"Profile\", Public = true)]\n public partial struct Profile\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Address Home;\n public Address Work;\n public Position Pos;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Profile.Insert(new Profile\n {\n Id = 1,\n Home = new Address { Street = \"1 Main\", Zip = 11111 },\n Work = new Address { Street = \"2 Broad\", Zip = 22222 },\n Pos = new Position { X = 7, Y = 9 }\n });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-015-product-type-columns-golden",
@@ -2077,20 +2038,6 @@
"work_dir_golden": "target/llm-runs/schema/t_015_product_type_columns/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_015_product_type_columns/csharp/server/gpt-5/llm",
"scorer_details": {
- "product_type_columns_row_parity": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "args": [],
- "golden_db": "schema-t-015-product-type-columns-golden",
- "golden_out": "Id | Home | Work | Pos ----+----------------------------------+-----------------------------------+---------------- 1 | (Street = \"1 Main\", Zip = 11111) | (Street = \"2 Broad\", Zip = 22222) | (X = 7, Y = 9)",
- "llm_db": "schema-t-015-product-type-columns-gpt-5-llm",
- "llm_out": "Id | Home | Work | Pos ----+----------------------------------+-----------------------------------+---------------- 1 | (Street = \"1 Main\", Zip = 11111) | (Street = \"2 Broad\", Zip = 22222) | (X = 7, Y = 9)",
- "query": "SELECT Id, Home, Work, Pos FROM profiles WHERE Id=1",
- "reducer": "Seed",
- "server": "http://127.0.0.1:37321"
- }
- },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -2099,34 +2046,41 @@
"llm_db": "schema-t-015-product-type-columns-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
},
"product_type_columns_row_count": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM profiles WHERE Id=1"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `profiles`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2007bfa13561ce0c8cb477e4e11171bc3733d8ba01683aecd94ed2d3f231319/sql)\n",
+ "phase": "sql"
+ }
+ },
+ "product_type_columns_row_parity": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `profiles`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200e5ea78db1095b274afeafb8041e139a0d7f11834b194b863882d4a7c3328/sql)\n",
+ "phase": "sql_golden"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:06:46.603867126Z",
- "finished_at": "2026-01-16T01:07:27.375238274Z"
+ "started_at": "2026-01-17T02:39:15.092804541Z",
+ "finished_at": "2026-01-17T02:40:02.303741426Z"
},
"t_016_sum_type_columns": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_016_sum_type_columns",
"lang": "csharp",
- "golden_published": false,
+ "golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 0,
- "llm_output": "using SpacetimeDB;\nusing Shape = SpacetimeDB.TaggedEnum<(Module.Circle, Module.Rectangle)>;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Circle\n {\n public int Radius;\n }\n\n [SpacetimeDB.Type]\n public partial struct Rectangle\n {\n public int Width;\n public int Height;\n }\n\n [SpacetimeDB.Table(Name = \"drawings\")]\n public partial struct Drawing\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Shape A;\n public Shape B;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n var a = new Circle { Radius = 10 };\n var b = new Rectangle { Width = 4, Height = 6 };\n ctx.Db.drawings.Insert(new Drawing\n {\n Id = 1,\n A = a,\n B = b\n });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Type]\n public partial struct Circle\n {\n public int Radius;\n }\n\n [SpacetimeDB.Type]\n public partial struct Rectangle\n {\n public int Width;\n public int Height;\n }\n\n [SpacetimeDB.Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }\n\n [SpacetimeDB.Table(Name = \"Drawing\")]\n public partial struct Drawing\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public Shape A;\n public Shape B;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Drawing.Insert(new Drawing\n {\n Id = 1,\n A = new Shape.Circle(new Circle { Radius = 10 }),\n B = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 })\n });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-016-sum-type-columns-golden",
@@ -2134,28 +2088,49 @@
"work_dir_golden": "target/llm-runs/schema/t_016_sum_type_columns/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm",
"scorer_details": {
- "publish_error": {
+ "sum_type_columns_row_parity": {
"pass": false,
"partial": 0.0,
"notes": {
- "error": "spacetime build (csharp) failed (exit=1)\n--- stderr ---\nError: command [\"dotnet\", \"publish\", \"-c\", \"Release\", \"-v\", \"quiet\"] exited with code 1\n\n--- stdout ---\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/FFI.cs(27,32): warning CS8981: The type name 'drawings' only contains lower-cased ascii characters. Such names may become reserved for the language. [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,188): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,206): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,227): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,252): error CS0246: The type or namespace name 'ARW' could not be found (are you missing a using directive or an assembly reference?) [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,58): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,73): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,92): error CS0426: The type name 'BSATN<,>' does not exist in the type 'TaggedEnum<(Circle, Rectangle)>' [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,99): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,114): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,134): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,149): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,167): error CS8657: Static member 'Module.Drawing.BSATN.BSATN(Module.Circle, Rectangle, Circle.BSATN, Rectangle.BSATN, ARW)' cannot be marked 'readonly'. [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,188): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,206): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,227): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,252): error CS0246: The type or namespace name 'BRW' could not be found (are you missing a using directive or an assembly reference?) [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,58): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,73): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,92): error CS0426: The type name 'BSATN<,>' does not exist in the type 'TaggedEnum<(Circle, Rectangle)>' [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,99): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,114): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,134): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,149): error CS0704: Cannot do non-virtual member lookup in 'Module' because it is a type parameter [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,167): error CS8657: Static member 'Module.Drawing.BSATN.BSATN(Module.Circle, Rectangle, Circle.BSATN, Rectangle.BSATN, BRW)' cannot be marked 'readonly'. [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,167): error CS0542: 'BSATN': member names cannot be the same as their enclosing type [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,167): error CS0542: 'BSATN': member names cannot be the same as their enclosing type [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(27,258): error CS1736: Default parameter value for '' must be a compile-time constant [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/obj/Release/net8.0/wasi-wasm/SpacetimeDB.Codegen/SpacetimeDB.Codegen.Module/Module.Drawing.cs(28,258): error CS1736: Default parameter value for '' must be a compile-time constant [/home/runner/work/SpacetimeDB/SpacetimeDB/target/llm-runs/schema/t_016_sum_type_columns/csharp/server/gpt-5/llm/StdbModule.csproj]\n",
- "phase": "build_or_publish"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `drawings`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2004da484f449e95616d5eb1c6832e7e06faf68e5ebbe454e63475d86740e6c/sql)\n",
+ "phase": "sql_golden"
+ }
+ },
+ "sum_type_columns_row_count": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `drawings`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2008ad55b1dba50ea8bb36552e0bc9480b27d8060b57d8ed5e9e4d86cb6b5f8/sql)\n",
+ "phase": "sql"
+ }
+ },
+ "schema_parity": {
+ "pass": true,
+ "partial": 1.0,
+ "notes": {
+ "golden_db": "schema-t-016-sum-type-columns-golden",
+ "llm_db": "schema-t-016-sum-type-columns-gpt-5-llm",
+ "reducers_diff": null,
+ "reducers_equal": true,
+ "server": "http://127.0.0.1:35677",
+ "tables_diff": null,
+ "tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:07:03.394897852Z",
- "finished_at": "2026-01-16T01:08:24.082465708Z"
+ "started_at": "2026-01-17T02:39:27.502655041Z",
+ "finished_at": "2026-01-17T02:40:24.282007063Z"
},
"t_017_scheduled_columns": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_017_scheduled_columns",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 2,
- "passed_tests": 2,
- "llm_output": "using System;\nusing SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"tick_timer\", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]\n public partial struct TickTimer\n {\n [SpacetimeDB.PrimaryKey]\n [SpacetimeDB.AutoInc]\n public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [SpacetimeDB.Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.tick_timer.Insert(new TickTimer\n {\n ScheduledId = 0,\n ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromMilliseconds(50))\n });\n }\n\n [SpacetimeDB.Reducer]\n public static void Tick(ReducerContext ctx, TickTimer tick)\n {\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using System;\nusing SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"TickTimer\", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]\n public partial struct TickTimer\n {\n [SpacetimeDB.PrimaryKey]\n [SpacetimeDB.AutoInc]\n public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [SpacetimeDB.Reducer]\n public static void Tick(ReducerContext ctx, TickTimer _timer)\n {\n }\n\n [SpacetimeDB.Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.TickTimer.Insert(new TickTimer\n {\n ScheduledAt = new ScheduleAt.Interval(TimeSpan.FromMilliseconds(50))\n });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-017-scheduled-columns-golden",
@@ -2163,6 +2138,14 @@
"work_dir_golden": "target/llm-runs/schema/t_017_scheduled_columns/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_017_scheduled_columns/csharp/server/gpt-5/llm",
"scorer_details": {
+ "scheduled_seeded_one_row": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `tick_timer`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20024b2c639ed44059f68c872d4ae8686e43def045ff88fb49f452645a17338/sql)\n",
+ "phase": "sql"
+ }
+ },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -2171,34 +2154,25 @@
"llm_db": "schema-t-017-scheduled-columns-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
- },
- "scheduled_seeded_one_row": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM tick_timer WHERE ScheduledId>=0"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:04:58.696136087Z",
- "finished_at": "2026-01-16T01:05:40.135697722Z"
+ "started_at": "2026-01-17T02:38:01.497464655Z",
+ "finished_at": "2026-01-17T02:38:36.670468145Z"
},
"t_018_constraints": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_018_constraints",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 3,
- "passed_tests": 3,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"accounts\")]\n [SpacetimeDB.Index.BTree(Name = \"by_name\", Columns = new[] { \"Name\" })]\n public partial struct Account\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n [SpacetimeDB.Unique]\n public string Email;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(SpacetimeDB.ReducerContext ctx)\n {\n ctx.Db.accounts.Insert(new Account { Id = 1, Email = \"a@example.com\", Name = \"Alice\" });\n ctx.Db.accounts.Insert(new Account { Id = 2, Email = \"b@example.com\", Name = \"Bob\" });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Account\")]\n [SpacetimeDB.Index.BTree(Name = \"by_name\", Columns = [\"Name\"])]\n public partial struct Account\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n [SpacetimeDB.Unique]\n public string Email;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Account.Insert(new Account { Id = 1, Email = \"a@example.com\", Name = \"Alice\" });\n ctx.Db.Account.Insert(new Account { Id = 2, Email = \"b@example.com\", Name = \"Bob\" });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-018-constraints-golden",
@@ -2206,29 +2180,6 @@
"work_dir_golden": "target/llm-runs/schema/t_018_constraints/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_018_constraints/csharp/server/gpt-5/llm",
"scorer_details": {
- "constraints_seed_two_rows": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM accounts WHERE Id=2"
- }
- },
- "constraints_row_parity_after_seed": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "args": [],
- "golden_db": "schema-t-018-constraints-golden",
- "golden_out": "Id | Email | Name ----+-----------------+--------- 1 | \"a@example.com\" | \"Alice\"",
- "llm_db": "schema-t-018-constraints-gpt-5-llm",
- "llm_out": "Id | Email | Name ----+-----------------+--------- 1 | \"a@example.com\" | \"Alice\"",
- "query": "SELECT Id, Email, Name FROM accounts WHERE Id=1",
- "reducer": "Seed",
- "server": "http://127.0.0.1:37321"
- }
- },
"schema_parity": {
"pass": true,
"partial": 1.0,
@@ -2237,25 +2188,41 @@
"llm_db": "schema-t-018-constraints-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
+ },
+ "constraints_row_parity_after_seed": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `accounts`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200db49191274d7a3a7263567507f9945c4e802cb3173928c835966c8331803/sql)\n",
+ "phase": "sql_golden"
+ }
+ },
+ "constraints_seed_two_rows": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `accounts`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20025629920e94d036ee70c22a88bf38265ab06fbb5cfc2c505e147018ad40c/sql)\n",
+ "phase": "sql"
+ }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:04:39.589075714Z",
- "finished_at": "2026-01-16T01:05:26.703647120Z"
+ "started_at": "2026-01-17T02:37:43.947065043Z",
+ "finished_at": "2026-01-17T02:38:24.847760366Z"
},
"t_019_many_to_many": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_019_many_to_many",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 5,
- "passed_tests": 5,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"users\")]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int UserId;\n public string Name;\n }\n\n [SpacetimeDB.Table(Name = \"groups\")]\n public partial struct Group\n {\n [SpacetimeDB.PrimaryKey]\n public int GroupId;\n public string Title;\n }\n\n [SpacetimeDB.Table(Name = \"memberships\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user\", Columns = new[] { \"UserId\" })]\n [SpacetimeDB.Index.BTree(Name = \"by_group\", Columns = new[] { \"GroupId\" })]\n public partial struct Membership\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int UserId;\n public int GroupId;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.users.Insert(new User { UserId = 1, Name = \"Alice\" });\n ctx.Db.users.Insert(new User { UserId = 2, Name = \"Bob\" });\n\n ctx.Db.groups.Insert(new Group { GroupId = 10, Title = \"Admin\" });\n ctx.Db.groups.Insert(new Group { GroupId = 20, Title = \"Dev\" });\n\n ctx.Db.memberships.Insert(new Membership { Id = 1, UserId = 1, GroupId = 10 });\n ctx.Db.memberships.Insert(new Membership { Id = 2, UserId = 1, GroupId = 20 });\n ctx.Db.memberships.Insert(new Membership { Id = 3, UserId = 2, GroupId = 20 });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"User\")]\n public partial struct User\n {\n [SpacetimeDB.PrimaryKey]\n public int UserId;\n public string Name;\n }\n\n [SpacetimeDB.Table(Name = \"Group\")]\n public partial struct Group\n {\n [SpacetimeDB.PrimaryKey]\n public int GroupId;\n public string Title;\n }\n\n [SpacetimeDB.Table(Name = \"Membership\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user\", Columns = new[] { \"UserId\" })]\n [SpacetimeDB.Index.BTree(Name = \"by_group\", Columns = new[] { \"GroupId\" })]\n public partial struct Membership\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int UserId;\n public int GroupId;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.User.Insert(new User { UserId = 1, Name = \"Alice\" });\n ctx.Db.User.Insert(new User { UserId = 2, Name = \"Bob\" });\n\n ctx.Db.Group.Insert(new Group { GroupId = 10, Title = \"Admin\" });\n ctx.Db.Group.Insert(new Group { GroupId = 20, Title = \"Dev\" });\n\n ctx.Db.Membership.Insert(new Membership { Id = 1, UserId = 1, GroupId = 10 });\n ctx.Db.Membership.Insert(new Membership { Id = 2, UserId = 1, GroupId = 20 });\n ctx.Db.Membership.Insert(new Membership { Id = 3, UserId = 2, GroupId = 20 });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-019-many-to-many-golden",
@@ -2263,40 +2230,36 @@
"work_dir_golden": "target/llm-runs/schema/t_019_many_to_many/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_019_many_to_many/csharp/server/gpt-5/llm",
"scorer_details": {
- "memberships_three_rows": {
- "pass": true,
- "partial": 1.0,
+ "m2m_has_1_20": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 3,
- "expected": 3,
- "sql": "SELECT COUNT(*) AS n FROM memberships"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `memberships`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2008ea9cb58625a6494cb7b2f3998b558db44fc3308f07f5e48fe0a03dee66e/sql)\n",
+ "phase": "sql"
}
},
- "m2m_has_1_20": {
- "pass": true,
- "partial": 1.0,
+ "m2m_has_2_20": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM memberships WHERE UserId=1 AND GroupId=20"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `memberships`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2008ea9cb58625a6494cb7b2f3998b558db44fc3308f07f5e48fe0a03dee66e/sql)\n",
+ "phase": "sql"
}
},
- "m2m_has_1_10": {
- "pass": true,
- "partial": 1.0,
+ "memberships_three_rows": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM memberships WHERE UserId=1 AND GroupId=10"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `memberships`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2008ea9cb58625a6494cb7b2f3998b558db44fc3308f07f5e48fe0a03dee66e/sql)\n",
+ "phase": "sql"
}
},
- "m2m_has_2_20": {
- "pass": true,
- "partial": 1.0,
+ "m2m_has_1_10": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM memberships WHERE UserId=2 AND GroupId=20"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `memberships`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c2008ea9cb58625a6494cb7b2f3998b558db44fc3308f07f5e48fe0a03dee66e/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -2307,25 +2270,25 @@
"llm_db": "schema-t-019-many-to-many-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:03:27.648698788Z",
- "finished_at": "2026-01-16T01:04:39.589013436Z"
+ "started_at": "2026-01-17T02:36:42.262573385Z",
+ "finished_at": "2026-01-17T02:37:43.946992007Z"
},
"t_020_ecs": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_020_ecs",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 5,
- "passed_tests": 5,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"entities\")]\n public partial struct Entity\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n }\n\n [SpacetimeDB.Table(Name = \"positions\")]\n public partial struct Position\n {\n [SpacetimeDB.PrimaryKey]\n public int EntityId;\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Table(Name = \"velocities\")]\n public partial struct Velocity\n {\n [SpacetimeDB.PrimaryKey]\n public int EntityId;\n public int VX;\n public int VY;\n }\n\n [SpacetimeDB.Table(Name = \"next_positions\")]\n public partial struct NextPosition\n {\n [SpacetimeDB.PrimaryKey]\n public int EntityId;\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n UpsertEntity(ctx, 1);\n UpsertPosition(ctx, 1, 0, 0);\n UpsertVelocity(ctx, 1, 1, 0);\n\n UpsertEntity(ctx, 2);\n UpsertPosition(ctx, 2, 10, 0);\n UpsertVelocity(ctx, 2, -2, 3);\n }\n\n [SpacetimeDB.Reducer]\n public static void Step(ReducerContext ctx)\n {\n foreach (var pos in ctx.Db.positions.Iter())\n {\n var vel = ctx.Db.velocities.EntityId.Find(pos.EntityId);\n if (!vel.HasValue)\n {\n continue;\n }\n\n var nx = pos.X + vel.Value.VX;\n var ny = pos.Y + vel.Value.VY;\n\n var existing = ctx.Db.next_positions.EntityId.Find(pos.EntityId);\n if (existing.HasValue)\n {\n var np = existing.Value;\n np.X = nx;\n np.Y = ny;\n ctx.Db.next_positions.EntityId.Update(np);\n }\n else\n {\n ctx.Db.next_positions.Insert(new NextPosition\n {\n EntityId = pos.EntityId,\n X = nx,\n Y = ny\n });\n }\n }\n }\n\n private static void UpsertEntity(ReducerContext ctx, int id)\n {\n var e = ctx.Db.entities.Id.Find(id);\n if (!e.HasValue)\n {\n ctx.Db.entities.Insert(new Entity { Id = id });\n }\n }\n\n private static void UpsertPosition(ReducerContext ctx, int entityId, int x, int y)\n {\n var p = ctx.Db.positions.EntityId.Find(entityId);\n if (p.HasValue)\n {\n var row = p.Value;\n row.X = x;\n row.Y = y;\n ctx.Db.positions.EntityId.Update(row);\n }\n else\n {\n ctx.Db.positions.Insert(new Position\n {\n EntityId = entityId,\n X = x,\n Y = y\n });\n }\n }\n\n private static void UpsertVelocity(ReducerContext ctx, int entityId, int vx, int vy)\n {\n var v = ctx.Db.velocities.EntityId.Find(entityId);\n if (v.HasValue)\n {\n var row = v.Value;\n row.VX = vx;\n row.VY = vy;\n ctx.Db.velocities.EntityId.Update(row);\n }\n else\n {\n ctx.Db.velocities.Insert(new Velocity\n {\n EntityId = entityId,\n VX = vx,\n VY = vy\n });\n }\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Entity\")]\n public partial struct Entity\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n }\n\n [SpacetimeDB.Table(Name = \"Position\")]\n public partial struct Position\n {\n [SpacetimeDB.PrimaryKey]\n public int EntityId;\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Table(Name = \"Velocity\")]\n public partial struct Velocity\n {\n [SpacetimeDB.PrimaryKey]\n public int EntityId;\n public int VX;\n public int VY;\n }\n\n [SpacetimeDB.Table(Name = \"NextPosition\")]\n public partial struct NextPosition\n {\n [SpacetimeDB.PrimaryKey]\n public int EntityId;\n public int X;\n public int Y;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n // Entity 1\n ctx.Db.Entity.Insert(new Entity { Id = 1 });\n ctx.Db.Position.Insert(new Position { EntityId = 1, X = 0, Y = 0 });\n ctx.Db.Velocity.Insert(new Velocity { EntityId = 1, VX = 1, VY = 0 });\n\n // Entity 2\n ctx.Db.Entity.Insert(new Entity { Id = 2 });\n ctx.Db.Position.Insert(new Position { EntityId = 2, X = 10, Y = 0 });\n ctx.Db.Velocity.Insert(new Velocity { EntityId = 2, VX = -2, VY = 3 });\n }\n\n [SpacetimeDB.Reducer]\n public static void Step(ReducerContext ctx)\n {\n foreach (var pos in ctx.Db.Position.Iter())\n {\n var vel = ctx.Db.Velocity.EntityId.Find(pos.EntityId);\n if (vel == null)\n {\n continue;\n }\n\n var nx = pos.X + vel.Value.VX;\n var ny = pos.Y + vel.Value.VY;\n\n var existing = ctx.Db.NextPosition.EntityId.Find(pos.EntityId);\n if (existing != null)\n {\n var row = existing.Value;\n row.X = nx;\n row.Y = ny;\n ctx.Db.NextPosition.EntityId.Update(row);\n }\n else\n {\n ctx.Db.NextPosition.Insert(new NextPosition\n {\n EntityId = pos.EntityId,\n X = nx,\n Y = ny\n });\n }\n }\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-020-ecs-golden",
@@ -2333,31 +2296,36 @@
"work_dir_golden": "target/llm-runs/schema/t_020_ecs/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_020_ecs/csharp/server/gpt-5/llm",
"scorer_details": {
- "ecs_seed_positions_count": {
- "pass": true,
- "partial": 1.0,
+ "ecs_step_next_positions_count": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 2,
- "expected": 2,
- "sql": "SELECT COUNT(*) AS n FROM positions"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `next_positions`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20091a11c03f51be06cd18ee4b36dc6b1e87d93f9897c479cb76452564e3605/sql)\n",
+ "phase": "sql"
}
},
- "ecs_next_pos_entity1": {
- "pass": true,
- "partial": 1.0,
+ "ecs_seed_positions_count": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM next_positions WHERE EntityId=1 AND X=1 AND Y=0"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `positions`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20091a11c03f51be06cd18ee4b36dc6b1e87d93f9897c479cb76452564e3605/sql)\n",
+ "phase": "sql"
}
},
"ecs_next_pos_entity2": {
- "pass": true,
- "partial": 1.0,
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM next_positions WHERE EntityId=2 AND X=8 AND Y=3"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `next_positions`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20091a11c03f51be06cd18ee4b36dc6b1e87d93f9897c479cb76452564e3605/sql)\n",
+ "phase": "sql"
+ }
+ },
+ "ecs_next_pos_entity1": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `next_positions`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c20091a11c03f51be06cd18ee4b36dc6b1e87d93f9897c479cb76452564e3605/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -2368,34 +2336,25 @@
"llm_db": "schema-t-020-ecs-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
- },
- "ecs_step_next_positions_count": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 2,
- "expected": 2,
- "sql": "SELECT COUNT(*) AS n FROM next_positions"
- }
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:03:27.652097672Z",
- "finished_at": "2026-01-16T01:04:58.695969194Z"
+ "started_at": "2026-01-17T02:36:42.266606487Z",
+ "finished_at": "2026-01-17T02:38:01.497277347Z"
},
"t_021_multi_column_index": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"task": "t_021_multi_column_index",
"lang": "csharp",
"golden_published": true,
"model_name": "GPT-5",
"total_tests": 4,
- "passed_tests": 4,
- "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"logs\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user_day\", Columns = [\"UserId\", \"Day\"])]\n public partial struct Log\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int UserId;\n public int Day;\n public string Message;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.logs.Insert(new Log { Id = 1, UserId = 7, Day = 1, Message = \"a\" });\n ctx.Db.logs.Insert(new Log { Id = 2, UserId = 7, Day = 2, Message = \"b\" });\n ctx.Db.logs.Insert(new Log { Id = 3, UserId = 9, Day = 1, Message = \"c\" });\n }\n}",
+ "passed_tests": 1,
+ "llm_output": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Log\", Public = true)]\n [SpacetimeDB.Index.BTree(Name = \"by_user_day\", Columns = new[] { \"UserId\", \"Day\" })]\n public partial struct Log\n {\n [SpacetimeDB.PrimaryKey]\n public int Id;\n public int UserId;\n public int Day;\n public string Message;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Log.Insert(new Log { Id = 1, UserId = 7, Day = 1, Message = \"a\" });\n ctx.Db.Log.Insert(new Log { Id = 2, UserId = 7, Day = 2, Message = \"b\" });\n ctx.Db.Log.Insert(new Log { Id = 3, UserId = 9, Day = 1, Message = \"c\" });\n }\n}",
"category": "schema",
"route_api_model": "gpt-5",
"golden_db": "schema-t-021-multi-column-index-golden",
@@ -2403,13 +2362,20 @@
"work_dir_golden": "target/llm-runs/schema/t_021_multi_column_index/csharp/server/golden",
"work_dir_llm": "target/llm-runs/schema/t_021_multi_column_index/csharp/server/gpt-5/llm",
"scorer_details": {
- "mcindex_lookup_u7_d2": {
- "pass": true,
- "partial": 1.0,
+ "mcindex_lookup_u7_d1": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM logs WHERE UserId=7 AND Day=2"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `logs`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200d69219860461311e299808a41ebae9617218c9ae2e796bc344b6340cc257/sql)\n",
+ "phase": "sql"
+ }
+ },
+ "mcindex_seed_count": {
+ "pass": false,
+ "partial": 0.0,
+ "notes": {
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `logs`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200d69219860461311e299808a41ebae9617218c9ae2e796bc344b6340cc257/sql)\n",
+ "phase": "sql"
}
},
"schema_parity": {
@@ -2420,33 +2386,23 @@
"llm_db": "schema-t-021-multi-column-index-gpt-5-llm",
"reducers_diff": null,
"reducers_equal": true,
- "server": "http://127.0.0.1:37321",
+ "server": "http://127.0.0.1:35677",
"tables_diff": null,
"tables_equal": true
}
},
- "mcindex_lookup_u7_d1": {
- "pass": true,
- "partial": 1.0,
- "notes": {
- "actual": 1,
- "expected": 1,
- "sql": "SELECT COUNT(*) AS n FROM logs WHERE UserId=7 AND Day=1"
- }
- },
- "mcindex_seed_count": {
- "pass": true,
- "partial": 1.0,
+ "mcindex_lookup_u7_d2": {
+ "pass": false,
+ "partial": 0.0,
"notes": {
- "actual": 3,
- "expected": 3,
- "sql": "SELECT COUNT(*) AS n FROM logs"
+ "error": "spacetime sql failed:\nWARNING: This command is UNSTABLE and subject to breaking changes.\n\nError: no such table: `logs`. If the table exists, it may be marked private.\n\nCaused by:\n HTTP status client error (400 Bad Request) for url (http://127.0.0.1:35677/v1/database/c200d69219860461311e299808a41ebae9617218c9ae2e796bc344b6340cc257/sql)\n",
+ "phase": "sql"
}
}
},
"vendor": "openai",
- "started_at": "2026-01-16T01:05:26.703696953Z",
- "finished_at": "2026-01-16T01:06:05.088463308Z"
+ "started_at": "2026-01-17T02:38:24.847804258Z",
+ "finished_at": "2026-01-17T02:38:56.019226354Z"
}
}
}
@@ -2547,87 +2503,87 @@
"syntax": "csharp"
},
"t_001_basic_tables": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct Users\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Table(Name = \"products\")]\n public partial struct Products\n {\n [PrimaryKey] public int Id;\n public string Title;\n public float Price;\n public bool InStock;\n }\n\n [Table(Name = \"notes\")]\n public partial struct Notes\n {\n [PrimaryKey] public int Id;\n public string Body;\n public long Rating;\n public bool Pinned;\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Table(Name = \"Product\")]\n public partial struct Product\n {\n [PrimaryKey] public int Id;\n public string Title;\n public float Price;\n public bool InStock;\n }\n\n [Table(Name = \"Note\")]\n public partial struct Note\n {\n [PrimaryKey] public int Id;\n public string Body;\n public long Rating;\n public bool Pinned;\n }\n}\n",
"syntax": "csharp"
},
"t_002_scheduled_table": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"tick_timer\", Scheduled = nameof(Tick), ScheduledAt = nameof(TickTimer.ScheduledAt))]\n public partial struct TickTimer\n {\n [PrimaryKey, AutoInc] public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [Reducer]\n public static void Tick(ReducerContext ctx, TickTimer timer) { }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n var interval = new TimeDuration { Microseconds = 50_000 };\n ctx.Db.tick_timer.Insert(new TickTimer\n {\n ScheduledAt = new ScheduleAt.Interval(interval)\n });\n }\n}",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"TickTimer\", Scheduled = nameof(Tick), ScheduledAt = nameof(TickTimer.ScheduledAt))]\n public partial struct TickTimer\n {\n [PrimaryKey, AutoInc] public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [Reducer]\n public static void Tick(ReducerContext ctx, TickTimer timer) { }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n var interval = new TimeDuration { Microseconds = 50_000 };\n ctx.Db.TickTimer.Insert(new TickTimer\n {\n ScheduledAt = new ScheduleAt.Interval(interval)\n });\n }\n}\n",
"syntax": "csharp"
},
"t_003_struct_in_table": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [Table(Name = \"entities\")]\n public partial struct Entity\n {\n [PrimaryKey] public int Id;\n public Position Pos;\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [Table(Name = \"Entity\")]\n public partial struct Entity\n {\n [PrimaryKey] public int Id;\n public Position Pos;\n }\n}\n",
"syntax": "csharp"
},
"t_004_insert": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void InsertUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n ctx.Db.users.Insert(new User { Id = id, Name = name, Age = age, Active = active });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void InsertUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n ctx.Db.User.Insert(new User { Id = id, Name = name, Age = age, Active = active });\n }\n}\n",
"syntax": "csharp"
},
"t_005_update": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void UpdateUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n ctx.Db.users.Id.Update(new User { Id = id, Name = name, Age = age, Active = active });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void UpdateUser(ReducerContext ctx, int id, string name, int age, bool active)\n {\n ctx.Db.User.Id.Update(new User { Id = id, Name = name, Age = age, Active = active });\n }\n}\n",
"syntax": "csharp"
},
"t_006_delete": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void DeleteUser(ReducerContext ctx, int id)\n {\n ctx.Db.users.Id.Delete(id);\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void DeleteUser(ReducerContext ctx, int id)\n {\n ctx.Db.User.Id.Delete(id);\n }\n}\n",
"syntax": "csharp"
},
"t_007_crud": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void Crud(ReducerContext ctx)\n {\n ctx.Db.users.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.users.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n ctx.Db.users.Id.Update(new User { Id = 1, Name = \"Alice2\", Age = 31, Active = false });\n ctx.Db.users.Id.Delete(2);\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer]\n public static void Crud(ReducerContext ctx)\n {\n ctx.Db.User.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.User.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n ctx.Db.User.Id.Update(new User { Id = 1, Name = \"Alice2\", Age = 31, Active = false });\n ctx.Db.User.Id.Delete(2);\n }\n}\n",
"syntax": "csharp"
},
"t_008_index_lookup": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Table(Name = \"results\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public string Name;\n }\n\n [Reducer]\n public static void LookupUserName(ReducerContext ctx, int id)\n {\n var u = ctx.Db.users.Id.Find(id);\n if (u.HasValue)\n {\n var row = u.Value;\n ctx.Db.results.Insert(new Result { Id = row.Id, Name = row.Name });\n }\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Table(Name = \"Result\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public string Name;\n }\n\n [Reducer]\n public static void LookupUserName(ReducerContext ctx, int id)\n {\n var u = ctx.Db.User.Id.Find(id);\n if (u.HasValue)\n {\n var row = u.Value;\n ctx.Db.Result.Insert(new Result { Id = row.Id, Name = row.Name });\n }\n }\n}\n",
"syntax": "csharp"
},
"t_009_init": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.users.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.users.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int Id;\n public string Name;\n public int Age;\n public bool Active;\n }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n ctx.Db.User.Insert(new User { Id = 1, Name = \"Alice\", Age = 30, Active = true });\n ctx.Db.User.Insert(new User { Id = 2, Name = \"Bob\", Age = 22, Active = false });\n }\n}\n",
"syntax": "csharp"
},
"t_010_connect": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"events\")]\n public partial struct Event\n {\n [PrimaryKey, AutoInc] public int Id;\n public string Kind;\n }\n\n [Reducer(ReducerKind.ClientConnected)]\n public static void ClientConnected(ReducerContext ctx)\n {\n ctx.Db.events.Insert(new Event { Kind = \"connected\" });\n }\n\n [Reducer(ReducerKind.ClientDisconnected)]\n public static void ClientDisconnected(ReducerContext ctx)\n {\n ctx.Db.events.Insert(new Event { Kind = \"disconnected\" });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"Event\")]\n public partial struct Event\n {\n [PrimaryKey, AutoInc] public int Id;\n public string Kind;\n }\n\n [Reducer(ReducerKind.ClientConnected)]\n public static void ClientConnected(ReducerContext ctx)\n {\n ctx.Db.Event.Insert(new Event { Kind = \"connected\" });\n }\n\n [Reducer(ReducerKind.ClientDisconnected)]\n public static void ClientDisconnected(ReducerContext ctx)\n {\n ctx.Db.Event.Insert(new Event { Kind = \"disconnected\" });\n }\n}\n",
"syntax": "csharp"
},
"t_011_helper_function": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"results\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public int Sum;\n }\n\n static int Add(int a, int b) => a + b;\n\n [Reducer]\n public static void ComputeSum(ReducerContext ctx, int id, int a, int b)\n {\n ctx.Db.results.Insert(new Result { Id = id, Sum = Add(a, b) });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"Result\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public int Sum;\n }\n\n static int Add(int a, int b) => a + b;\n\n [Reducer]\n public static void ComputeSum(ReducerContext ctx, int id, int a, int b)\n {\n ctx.Db.Result.Insert(new Result { Id = id, Sum = Add(a, b) });\n }\n}\n",
"syntax": "csharp"
},
"t_012_spacetime_product_type": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Score\n {\n public int Left;\n public int Right;\n }\n\n [Table(Name = \"results\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public Score Value;\n }\n\n [Reducer]\n public static void SetScore(ReducerContext ctx, int id, int left, int right)\n {\n ctx.Db.results.Insert(new Result { Id = id, Value = new Score { Left = left, Right = right } });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Score\n {\n public int Left;\n public int Right;\n }\n\n [Table(Name = \"Result\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public Score Value;\n }\n\n [Reducer]\n public static void SetScore(ReducerContext ctx, int id, int left, int right)\n {\n ctx.Db.Result.Insert(new Result { Id = id, Value = new Score { Left = left, Right = right } });\n }\n}\n",
"syntax": "csharp"
},
"t_013_spacetime_sum_type": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Circle { public int Radius; }\n\n [Type]\n public partial struct Rectangle { public int Width; public int Height; }\n\n [Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> {}\n\n [Table(Name = \"results\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public Shape Value;\n }\n\n [Reducer]\n public static void SetCircle(ReducerContext ctx, int id, int radius)\n {\n ctx.Db.results.Insert(new Result { Id = id, Value = new Shape.Circle(new Circle { Radius = radius }) });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Circle { public int Radius; }\n\n [Type]\n public partial struct Rectangle { public int Width; public int Height; }\n\n [Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> {}\n\n [Table(Name = \"Result\")]\n public partial struct Result\n {\n [PrimaryKey] public int Id;\n public Shape Value;\n }\n\n [Reducer]\n public static void SetCircle(ReducerContext ctx, int id, int radius)\n {\n ctx.Db.Result.Insert(new Result { Id = id, Value = new Shape.Circle(new Circle { Radius = radius }) });\n }\n}\n",
"syntax": "csharp"
},
"t_014_elementary_columns": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"primitives\")]\n public partial struct Primitive\n {\n [PrimaryKey] public int Id;\n public int Count;\n public long Total;\n public float Price;\n public double Ratio;\n public bool Active;\n public string Name;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.primitives.Insert(new Primitive {\n Id = 1,\n Count = 2,\n Total = 3000000000,\n Price = 1.5f,\n Ratio = 2.25,\n Active = true,\n Name = \"Alice\"\n });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"Primitive\")]\n public partial struct Primitive\n {\n [PrimaryKey] public int Id;\n public int Count;\n public long Total;\n public float Price;\n public double Ratio;\n public bool Active;\n public string Name;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Primitive.Insert(new Primitive {\n Id = 1,\n Count = 2,\n Total = 3000000000,\n Price = 1.5f,\n Ratio = 2.25,\n Active = true,\n Name = \"Alice\"\n });\n }\n}\n",
"syntax": "csharp"
},
"t_015_product_type_columns": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Address\n {\n public string Street;\n public int Zip;\n }\n\n [Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [Table(Name = \"profiles\")]\n public partial struct Profile\n {\n [PrimaryKey] public int Id;\n public Address Home;\n public Address Work;\n public Position Pos;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.profiles.Insert(new Profile {\n Id = 1,\n Home = new Address { Street = \"1 Main\", Zip = 11111 },\n Work = new Address { Street = \"2 Broad\", Zip = 22222 },\n Pos = new Position { X = 7, Y = 9 }\n });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Address\n {\n public string Street;\n public int Zip;\n }\n\n [Type]\n public partial struct Position\n {\n public int X;\n public int Y;\n }\n\n [Table(Name = \"Profile\")]\n public partial struct Profile\n {\n [PrimaryKey] public int Id;\n public Address Home;\n public Address Work;\n public Position Pos;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Profile.Insert(new Profile {\n Id = 1,\n Home = new Address { Street = \"1 Main\", Zip = 11111 },\n Work = new Address { Street = \"2 Broad\", Zip = 22222 },\n Pos = new Position { X = 7, Y = 9 }\n });\n }\n}\n",
"syntax": "csharp"
},
"t_016_sum_type_columns": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Circle { public int Radius; }\n\n [Type]\n public partial struct Rectangle { public int Width; public int Height; }\n\n [Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> {}\n\n [Table(Name = \"drawings\")]\n public partial struct Drawing\n {\n [PrimaryKey] public int Id;\n public Shape A;\n public Shape B;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.drawings.Insert(new Drawing {\n Id = 1,\n A = new Shape.Circle(new Circle { Radius = 10 }),\n B = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 })\n });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Type]\n public partial struct Circle { public int Radius; }\n\n [Type]\n public partial struct Rectangle { public int Width; public int Height; }\n\n [Type]\n public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> {}\n\n [Table(Name = \"Drawing\")]\n public partial struct Drawing\n {\n [PrimaryKey] public int Id;\n public Shape A;\n public Shape B;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Drawing.Insert(new Drawing {\n Id = 1,\n A = new Shape.Circle(new Circle { Radius = 10 }),\n B = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 })\n });\n }\n}\n",
"syntax": "csharp"
},
"t_017_scheduled_columns": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"tick_timer\", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]\n public partial struct TickTimer\n {\n [PrimaryKey, AutoInc] public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [Reducer]\n public static void Tick(ReducerContext ctx, TickTimer schedule) { }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n var interval = new TimeDuration { Microseconds = 50_000 };\n ctx.Db.tick_timer.Insert(new TickTimer\n {\n ScheduledId = 0,\n ScheduledAt = new ScheduleAt.Interval(interval)\n });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"TickTimer\", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]\n public partial struct TickTimer\n {\n [PrimaryKey, AutoInc] public ulong ScheduledId;\n public ScheduleAt ScheduledAt;\n }\n\n [Reducer]\n public static void Tick(ReducerContext ctx, TickTimer schedule) { }\n\n [Reducer(ReducerKind.Init)]\n public static void Init(ReducerContext ctx)\n {\n var interval = new TimeDuration { Microseconds = 50_000 };\n ctx.Db.TickTimer.Insert(new TickTimer\n {\n ScheduledId = 0,\n ScheduledAt = new ScheduleAt.Interval(interval)\n });\n }\n}\n",
"syntax": "csharp"
},
"t_018_constraints": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"accounts\", Public = true)]\n [SpacetimeDB.Index.BTree(Name = \"by_name\", Columns = [nameof(Name)])]\n public partial struct Account\n {\n [SpacetimeDB.PrimaryKey] public int Id;\n [SpacetimeDB.Unique] public string Email;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.accounts.Insert(new Account { Id = 1, Email = \"a@example.com\", Name = \"Alice\" });\n ctx.Db.accounts.Insert(new Account { Id = 2, Email = \"b@example.com\", Name = \"Bob\" });\n }\n}",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [SpacetimeDB.Table(Name = \"Account\", Public = true)]\n [SpacetimeDB.Index.BTree(Name = \"by_name\", Columns = [nameof(Name)])]\n public partial struct Account\n {\n [SpacetimeDB.PrimaryKey] public int Id;\n [SpacetimeDB.Unique] public string Email;\n public string Name;\n }\n\n [SpacetimeDB.Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Account.Insert(new Account { Id = 1, Email = \"a@example.com\", Name = \"Alice\" });\n ctx.Db.Account.Insert(new Account { Id = 2, Email = \"b@example.com\", Name = \"Bob\" });\n }\n}\n",
"syntax": "csharp"
},
"t_019_many_to_many": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"users\")]\n public partial struct User\n {\n [PrimaryKey] public int UserId;\n public string Name;\n }\n\n [Table(Name = \"groups\")]\n public partial struct Group\n {\n [PrimaryKey] public int GroupId;\n public string Title;\n }\n\n [Table(Name = \"memberships\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user\", Columns = new[] { nameof(UserId) })]\n [SpacetimeDB.Index.BTree(Name = \"by_group\", Columns = new[] { nameof(GroupId) })]\n public partial struct Membership\n {\n [PrimaryKey] public int Id;\n public int UserId;\n public int GroupId;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.users.Insert(new User { UserId = 1, Name = \"Alice\" });\n ctx.Db.users.Insert(new User { UserId = 2, Name = \"Bob\" });\n\n ctx.Db.groups.Insert(new Group { GroupId = 10, Title = \"Admin\" });\n ctx.Db.groups.Insert(new Group { GroupId = 20, Title = \"Dev\" });\n\n ctx.Db.memberships.Insert(new Membership { Id = 1, UserId = 1, GroupId = 10 });\n ctx.Db.memberships.Insert(new Membership { Id = 2, UserId = 1, GroupId = 20 });\n ctx.Db.memberships.Insert(new Membership { Id = 3, UserId = 2, GroupId = 20 });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"User\")]\n public partial struct User\n {\n [PrimaryKey] public int UserId;\n public string Name;\n }\n\n [Table(Name = \"Group\")]\n public partial struct Group\n {\n [PrimaryKey] public int GroupId;\n public string Title;\n }\n\n [Table(Name = \"Membership\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user\", Columns = new[] { nameof(UserId) })]\n [SpacetimeDB.Index.BTree(Name = \"by_group\", Columns = new[] { nameof(GroupId) })]\n public partial struct Membership\n {\n [PrimaryKey] public int Id;\n public int UserId;\n public int GroupId;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.User.Insert(new User { UserId = 1, Name = \"Alice\" });\n ctx.Db.User.Insert(new User { UserId = 2, Name = \"Bob\" });\n\n ctx.Db.Group.Insert(new Group { GroupId = 10, Title = \"Admin\" });\n ctx.Db.Group.Insert(new Group { GroupId = 20, Title = \"Dev\" });\n\n ctx.Db.Membership.Insert(new Membership { Id = 1, UserId = 1, GroupId = 10 });\n ctx.Db.Membership.Insert(new Membership { Id = 2, UserId = 1, GroupId = 20 });\n ctx.Db.Membership.Insert(new Membership { Id = 3, UserId = 2, GroupId = 20 });\n }\n}\n",
"syntax": "csharp"
},
"t_020_ecs": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"entities\")]\n public partial struct Entity { [PrimaryKey] public int Id; }\n\n [Table(Name = \"positions\")]\n public partial struct Position\n {\n [PrimaryKey] public int EntityId;\n public int X;\n public int Y;\n }\n\n [Table(Name = \"velocities\")]\n public partial struct Velocity\n {\n [PrimaryKey] public int EntityId;\n public int VX;\n public int VY;\n }\n\n [Table(Name = \"next_positions\")]\n public partial struct NextPosition\n {\n [PrimaryKey] public int EntityId;\n public int X;\n public int Y;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.entities.Insert(new Entity { Id = 1 });\n ctx.Db.entities.Insert(new Entity { Id = 2 });\n\n ctx.Db.positions.Insert(new Position { EntityId = 1, X = 0, Y = 0 });\n ctx.Db.positions.Insert(new Position { EntityId = 2, X = 10, Y = 0 });\n\n ctx.Db.velocities.Insert(new Velocity { EntityId = 1, VX = 1, VY = 0 });\n ctx.Db.velocities.Insert(new Velocity { EntityId = 2, VX = -2, VY = 3 });\n }\n\n [Reducer]\n public static void Step(ReducerContext ctx)\n {\n foreach (var p in ctx.Db.positions.Iter())\n {\n var velOpt = ctx.Db.velocities.EntityId.Find(p.EntityId);\n if (!velOpt.HasValue) continue;\n\n var np = new NextPosition {\n EntityId = p.EntityId,\n X = p.X + velOpt.Value.VX,\n Y = p.Y + velOpt.Value.VY\n };\n\n if (ctx.Db.next_positions.EntityId.Find(p.EntityId).HasValue)\n ctx.Db.next_positions.EntityId.Update(np);\n else\n ctx.Db.next_positions.Insert(np);\n }\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"Entity\")]\n public partial struct Entity { [PrimaryKey] public int Id; }\n\n [Table(Name = \"Position\")]\n public partial struct Position\n {\n [PrimaryKey] public int EntityId;\n public int X;\n public int Y;\n }\n\n [Table(Name = \"Velocity\")]\n public partial struct Velocity\n {\n [PrimaryKey] public int EntityId;\n public int VX;\n public int VY;\n }\n\n [Table(Name = \"NextPosition\")]\n public partial struct NextPosition\n {\n [PrimaryKey] public int EntityId;\n public int X;\n public int Y;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Entity.Insert(new Entity { Id = 1 });\n ctx.Db.Entity.Insert(new Entity { Id = 2 });\n\n ctx.Db.Position.Insert(new Position { EntityId = 1, X = 0, Y = 0 });\n ctx.Db.Position.Insert(new Position { EntityId = 2, X = 10, Y = 0 });\n\n ctx.Db.Velocity.Insert(new Velocity { EntityId = 1, VX = 1, VY = 0 });\n ctx.Db.Velocity.Insert(new Velocity { EntityId = 2, VX = -2, VY = 3 });\n }\n\n [Reducer]\n public static void Step(ReducerContext ctx)\n {\n foreach (var p in ctx.Db.Position.Iter())\n {\n var velOpt = ctx.Db.Velocity.EntityId.Find(p.EntityId);\n if (!velOpt.HasValue) continue;\n\n var np = new NextPosition {\n EntityId = p.EntityId,\n X = p.X + velOpt.Value.VX,\n Y = p.Y + velOpt.Value.VY\n };\n\n if (ctx.Db.NextPosition.EntityId.Find(p.EntityId).HasValue)\n ctx.Db.NextPosition.EntityId.Update(np);\n else\n ctx.Db.NextPosition.Insert(np);\n }\n }\n}\n",
"syntax": "csharp"
},
"t_021_multi_column_index": {
- "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"logs\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user_day\", Columns = new[] { nameof(UserId), nameof(Day) })]\n public partial struct Log\n {\n [PrimaryKey] public int Id;\n public int UserId;\n public int Day;\n public string Message;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.logs.Insert(new Log { Id = 1, UserId = 7, Day = 1, Message = \"a\" });\n ctx.Db.logs.Insert(new Log { Id = 2, UserId = 7, Day = 2, Message = \"b\" });\n ctx.Db.logs.Insert(new Log { Id = 3, UserId = 9, Day = 1, Message = \"c\" });\n }\n}\n",
+ "answer": "using SpacetimeDB;\n\npublic static partial class Module\n{\n [Table(Name = \"Log\")]\n [SpacetimeDB.Index.BTree(Name = \"by_user_day\", Columns = new[] { nameof(UserId), nameof(Day) })]\n public partial struct Log\n {\n [PrimaryKey] public int Id;\n public int UserId;\n public int Day;\n public string Message;\n }\n\n [Reducer]\n public static void Seed(ReducerContext ctx)\n {\n ctx.Db.Log.Insert(new Log { Id = 1, UserId = 7, Day = 1, Message = \"a\" });\n ctx.Db.Log.Insert(new Log { Id = 2, UserId = 7, Day = 2, Message = \"b\" });\n ctx.Db.Log.Insert(new Log { Id = 3, UserId = 9, Day = 1, Message = \"c\" });\n }\n}\n",
"syntax": "csharp"
}
}
diff --git a/docs/llms/docs-benchmark-summary.json b/docs/llms/docs-benchmark-summary.json
index 3f8c0800dbc..30e341ff193 100644
--- a/docs/llms/docs-benchmark-summary.json
+++ b/docs/llms/docs-benchmark-summary.json
@@ -1,38 +1,38 @@
{
"version": 1,
- "generated_at": "2026-01-16T01:11:54.962Z",
+ "generated_at": "2026-01-17T02:44:26.905Z",
"by_language": {
"csharp": {
"modes": {
"docs": {
- "hash": "57aa5b6bb986daddf9d3a9f4992cdb0c6bab78257186c00ab1284c1b40281b46",
+ "hash": "7bd1056c239ec41df56d3be0edd5b7aac1c433acc3efbac0fe6f3b2a79ca2f1f",
"models": {
"GPT-5": {
"categories": {
"basics": {
"tasks": 12,
"total_tests": 27,
- "passed_tests": 27,
- "pass_pct": 100.0,
- "task_pass_equiv": 12.0,
- "task_pass_pct": 100.0
+ "passed_tests": 12,
+ "pass_pct": 44.444443,
+ "task_pass_equiv": 7.333334,
+ "task_pass_pct": 61.111115
},
"schema": {
"tasks": 10,
"total_tests": 34,
- "passed_tests": 28,
- "pass_pct": 82.35294,
- "task_pass_equiv": 8.0,
- "task_pass_pct": 80.0
+ "passed_tests": 10,
+ "pass_pct": 29.411764,
+ "task_pass_equiv": 3.15,
+ "task_pass_pct": 31.5
}
},
"totals": {
"tasks": 22,
"total_tests": 61,
- "passed_tests": 55,
- "pass_pct": 90.16393,
- "task_pass_equiv": 20.0,
- "task_pass_pct": 90.909096
+ "passed_tests": 22,
+ "pass_pct": 36.065575,
+ "task_pass_equiv": 10.483333,
+ "task_pass_pct": 47.651512
}
}
}
@@ -49,10 +49,10 @@
"basics": {
"tasks": 12,
"total_tests": 27,
- "passed_tests": 26,
- "pass_pct": 96.296295,
- "task_pass_equiv": 11.0,
- "task_pass_pct": 91.66667
+ "passed_tests": 25,
+ "pass_pct": 92.59259,
+ "task_pass_equiv": 10.0,
+ "task_pass_pct": 83.33333
},
"schema": {
"tasks": 10,
@@ -66,10 +66,10 @@
"totals": {
"tasks": 22,
"total_tests": 61,
- "passed_tests": 55,
- "pass_pct": 90.16393,
- "task_pass_equiv": 19.0,
- "task_pass_pct": 86.36364
+ "passed_tests": 54,
+ "pass_pct": 88.52459,
+ "task_pass_equiv": 18.0,
+ "task_pass_pct": 81.818184
}
}
}
diff --git a/docs/src/components/Check.tsx b/docs/src/components/Check.tsx
new file mode 100644
index 00000000000..46062f4ce9e
--- /dev/null
+++ b/docs/src/components/Check.tsx
@@ -0,0 +1,45 @@
+import React from "react";
+
+/**
+ * A green checkmark badge for use in tables.
+ *
+ * Usage in MDX:
+ * ```mdx
+ * import { Check } from "@site/src/components/Check";
+ *
+ * | Feature | Supported |
+ * |---------|-----------|
+ * | Thing | |
+ * ```
+ */
+export function Check() {
+ return (
+
+ );
+}
+
+export default Check;
diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css
index 5323f2a9a95..81afdbbbc6d 100644
--- a/docs/src/css/custom.css
+++ b/docs/src/css/custom.css
@@ -538,44 +538,57 @@ ul.theme-doc-sidebar-menu {
table {
display: table;
- border-collapse: collapse;
+ border-collapse: separate;
border-spacing: 0;
width: 100%;
- color: var(--clockworklabs-color-white);
letter-spacing: -0.01em;
margin-top: 40px;
margin-bottom: 40px;
font-size: 16px;
- line-height: 1.75;
+ line-height: var(--24px);
+ color: var(--clockworklabs-color-white);
+ border: 1px solid var(--clockworklabs-color-n6);
+ border-radius: 8px;
+ overflow: hidden;
+}
- thead {
- color: var(--clockworklabs-strong-color);
- text-align: left;
+table th,
+table td {
+ border: none;
+ text-align: left;
+ padding: 12px 24px;
+}
- tr {
- border-bottom: none;
- }
- }
+table thead {
+ background-color: var(--clockworklabs-color-shade1);
+ color: var(--clockworklabs-color-n4);
+}
- tr {
- border-top: none;
- }
+table thead tr {
+ border-bottom: none;
+}
- td {
- border: 1px solid var(--clockworklabs-color-n6);
- padding: 0.5rem 0.75rem;
+table tbody tr {
+ border-top: 1px solid var(--clockworklabs-color-n6);
+}
- code {
- background-color: var(--clockworklabs-code-background-color);
- color: var(--clockworklabs-code-color);
- border: 1px solid var(--clockworklabs-code-border-color);
- }
- }
+table tbody tr td {
+ border-top: 1px solid var(--clockworklabs-color-n6);
+}
- th {
- border: 1px solid var(--clockworklabs-color-n6);
- padding: 0.5rem 0.75rem;
- }
+table tbody tr:nth-child(2n) {
+ background-color: var(--clockworklabs-color-shade2);
+}
+
+table tbody td code {
+ background-color: var(--clockworklabs-code-background-color);
+ color: var(--clockworklabs-code-color);
+ border: 1px solid var(--clockworklabs-code-border-color);
+}
+
+/* Green accent class for special text */
+table .table-accent {
+ color: var(--clockworklabs-color-green);
}
/* Make the page content left aligned */
diff --git a/docs/static/ai-rules/spacetimedb-csharp.mdc b/docs/static/ai-rules/spacetimedb-csharp.mdc
new file mode 100644
index 00000000000..7e4d987bb22
--- /dev/null
+++ b/docs/static/ai-rules/spacetimedb-csharp.mdc
@@ -0,0 +1,580 @@
+---
+description: "MANDATORY: Read this ENTIRE file before writing ANY SpacetimeDB C# code. Contains critical SDK patterns and HALLUCINATED APIs to avoid."
+globs: **/*.cs
+alwaysApply: true
+---
+
+# SpacetimeDB C# SDK
+
+> **Tested with:** SpacetimeDB runtime 1.11.x, .NET 8 SDK
+> **Last updated:** 2026-01-14
+
+---
+
+## HALLUCINATED APIs — DO NOT USE
+
+**These APIs DO NOT EXIST. LLMs frequently hallucinate them.**
+
+```csharp
+// WRONG — these do not exist
+[SpacetimeDB.Procedure] // C# does NOT support procedures yet!
+ctx.db.tableName // Wrong casing, should be PascalCase
+ctx.Db.tableName.Get(id) // Use Find, not Get
+ctx.Db.TableName.FindById(id) // Use index accessor: ctx.Db.TableName.Id.Find(id)
+ctx.Db.table.field_name.Find(x) // Wrong! Use PascalCase: ctx.Db.Table.FieldName.Find(x)
+Optional field; // Use C# nullable: string? field
+
+// WRONG — missing partial keyword
+public struct MyTable { } // Must be "partial struct"
+public class Module { } // Must be "static partial class"
+
+// WRONG — non-partial types
+[SpacetimeDB.Table(Name = "player")]
+public struct Player { } // WRONG — missing partial!
+
+// WRONG — sum type syntax (VERY COMMON MISTAKE)
+public partial struct Shape : TaggedEnum<(Circle, Rectangle)> { } // WRONG: struct, missing names
+public partial record Shape : TaggedEnum<(Circle, Rectangle)> { } // WRONG: missing variant names
+public partial class Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { } // WRONG: class
+
+// WRONG — Index attribute without full qualification
+[Index.BTree(Name = "idx", Columns = new[] { "Col" })] // Ambiguous with System.Index!
+[Index.BTree(Name = "idx", Columns = ["Col"])] // Collection expressions don't work in attributes!
+```
+
+### CORRECT PATTERNS:
+
+```csharp
+// CORRECT IMPORTS
+using SpacetimeDB;
+
+// CORRECT TABLE — must be partial struct
+[SpacetimeDB.Table(Name = "player", Public = true)]
+public partial struct Player
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ public Identity OwnerId;
+ public string Name;
+}
+
+// CORRECT MODULE — must be static partial class
+public static partial class Module
+{
+ [SpacetimeDB.Reducer]
+ public static void CreatePlayer(ReducerContext ctx, string name)
+ {
+ ctx.Db.Player.Insert(new Player { Id = 0, OwnerId = ctx.Sender, Name = name });
+ }
+}
+
+// CORRECT DATABASE ACCESS — PascalCase, index-based lookups
+var player = ctx.Db.Player.Id.Find(playerId);
+var player = ctx.Db.Player.OwnerId.Find(ctx.Sender);
+```
+
+### DO NOT:
+- **Forget `partial` keyword** — required on all tables and Module class
+- **Use lowercase table access** — `ctx.Db.Player` not `ctx.Db.player`
+- **Try to use procedures** — C# does not support procedures yet
+- **Use `Optional`** — use C# nullable syntax `T?` instead
+
+---
+
+## 1) Common Mistakes Table
+
+### Server-side errors
+
+| Wrong | Right | Error |
+|-------|-------|-------|
+| Missing `partial` keyword | `public partial struct Table` | Generated code won't compile |
+| `ctx.Db.player` (lowercase) | `ctx.Db.Player` (PascalCase) | Property not found |
+| `Optional` | `string?` | Type not found |
+| `ctx.Db.Table.Get(id)` | `ctx.Db.Table.Id.Find(id)` | Method not found |
+| Wrong .csproj name | `StdbModule.csproj` | Publish fails silently |
+| .NET 9 SDK | .NET 8 SDK only | WASI compilation fails |
+| Missing WASI workload | `dotnet workload install wasi-experimental` | Build fails |
+| `[Procedure]` attribute | Reducers only | Procedures not supported in C# |
+| Missing `Public = true` | Add to `[Table]` attribute | Clients can't subscribe |
+| Using `Random` | Avoid non-deterministic code | Sandbox violation |
+| async/await in reducers | Synchronous only | Not supported |
+| `[Index.BTree(...)]` | `[SpacetimeDB.Index.BTree(...)]` | Ambiguous with System.Index |
+| `Columns = ["A", "B"]` | `Columns = new[] { "A", "B" }` | Collection expressions invalid in attributes |
+| `partial struct : TaggedEnum` | `partial record : TaggedEnum` | Sum types must be record |
+| `TaggedEnum<(A, B)>` | `TaggedEnum<(A A, B B)>` | Tuple must include variant names |
+
+### Client-side errors
+
+| Wrong | Right | Error |
+|-------|-------|-------|
+| Wrong namespace | `using SpacetimeDB.ClientApi;` | Types not found |
+
+---
+
+## 2) Table Definition (CRITICAL)
+
+**Tables MUST use `partial struct` or `partial class` for code generation.**
+
+```csharp
+using SpacetimeDB;
+
+// WRONG — missing partial!
+[SpacetimeDB.Table(Name = "player")]
+public struct Player { } // Will not generate properly!
+
+// RIGHT — with partial keyword
+[SpacetimeDB.Table(Name = "player", Public = true)]
+public partial struct Player
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ public Identity OwnerId;
+ public string Name;
+ public Timestamp CreatedAt;
+}
+
+// With indexes
+[SpacetimeDB.Table(Name = "task", Public = true)]
+public partial struct Task
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ [SpacetimeDB.Index.BTree]
+ public Identity OwnerId;
+
+ public string Title;
+ public bool Completed;
+}
+
+// Multi-column index
+[SpacetimeDB.Table(Name = "score", Public = true)]
+[SpacetimeDB.Index.BTree(Name = "by_player_game", Columns = new[] { "PlayerId", "GameId" })]
+public partial struct Score
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ public Identity PlayerId;
+ public string GameId;
+ public int Points;
+}
+```
+
+### Field attributes
+
+```csharp
+[SpacetimeDB.PrimaryKey] // Exactly one per table (required)
+[SpacetimeDB.AutoInc] // Auto-increment (integer fields only)
+[SpacetimeDB.Unique] // Unique constraint
+[SpacetimeDB.Index.BTree] // Single-column B-tree index
+[SpacetimeDB.Default(value)] // Default value for new columns
+```
+
+### Column types
+
+```csharp
+byte, sbyte, short, ushort // 8/16-bit integers
+int, uint, long, ulong // 32/64-bit integers
+float, double // Floats
+bool // Boolean
+string // Text
+Identity // User identity
+Timestamp // Timestamp
+ScheduleAt // For scheduled tables
+T? // Nullable (e.g., string?)
+List // Arrays
+```
+
+### Insert with auto-increment
+
+```csharp
+// Insert returns the row with generated ID
+var player = ctx.Db.Player.Insert(new Player
+{
+ Id = 0, // Pass 0 to trigger auto-increment
+ OwnerId = ctx.Sender,
+ Name = name,
+ CreatedAt = ctx.Timestamp
+});
+ulong newId = player.Id; // Get actual generated ID
+```
+
+---
+
+## 3) Module and Reducers
+
+**The Module class MUST be `public static partial class`.**
+
+```csharp
+using SpacetimeDB;
+
+public static partial class Module
+{
+ [SpacetimeDB.Reducer]
+ public static void CreateTask(ReducerContext ctx, string title)
+ {
+ // Validate
+ if (string.IsNullOrEmpty(title))
+ {
+ throw new Exception("Title cannot be empty"); // Rolls back transaction
+ }
+
+ // Insert
+ ctx.Db.Task.Insert(new Task
+ {
+ Id = 0,
+ OwnerId = ctx.Sender,
+ Title = title,
+ Completed = false
+ });
+ }
+
+ [SpacetimeDB.Reducer]
+ public static void CompleteTask(ReducerContext ctx, ulong taskId)
+ {
+ var task = ctx.Db.Task.Id.Find(taskId);
+ if (task is null)
+ {
+ throw new Exception("Task not found");
+ }
+
+ if (task.Value.OwnerId != ctx.Sender)
+ {
+ throw new Exception("Not authorized");
+ }
+
+ ctx.Db.Task.Id.Update(task.Value with { Completed = true });
+ }
+
+ [SpacetimeDB.Reducer]
+ public static void DeleteTask(ReducerContext ctx, ulong taskId)
+ {
+ ctx.Db.Task.Id.Delete(taskId);
+ }
+}
+```
+
+### Lifecycle reducers
+
+```csharp
+public static partial class Module
+{
+ [SpacetimeDB.Reducer(ReducerKind.Init)]
+ public static void Init(ReducerContext ctx)
+ {
+ // Called once when module is first published
+ Log.Info("Module initialized");
+ }
+
+ [SpacetimeDB.Reducer(ReducerKind.ClientConnected)]
+ public static void OnConnect(ReducerContext ctx)
+ {
+ // ctx.Sender is the connecting client
+ Log.Info($"Client connected: {ctx.Sender}");
+ }
+
+ [SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)]
+ public static void OnDisconnect(ReducerContext ctx)
+ {
+ // Clean up client state
+ Log.Info($"Client disconnected: {ctx.Sender}");
+ }
+}
+```
+
+### ReducerContext API
+
+```csharp
+ctx.Sender // Identity of the caller
+ctx.Timestamp // Current timestamp
+ctx.Db // Database access
+ctx.Identity // Module's own identity
+ctx.ConnectionId // Connection ID (nullable)
+```
+
+---
+
+## 4) Database Access
+
+### Naming convention
+- **Tables**: Use PascalCase singular names in the `Name` attribute
+ - `[Table(Name = "User")]` → `ctx.Db.User`
+ - `[Table(Name = "PlayerStats")]` → `ctx.Db.PlayerStats`
+- **Indexes**: PascalCase, match field name
+ - Field `OwnerId` with `[Index.BTree]` → `ctx.Db.User.OwnerId`
+
+### Primary key operations
+
+```csharp
+// Find by primary key — returns nullable
+if (ctx.Db.Task.Id.Find(taskId) is Task task)
+{
+ // Use task
+}
+
+// Update by primary key
+ctx.Db.Task.Id.Update(updatedTask);
+
+// Delete by primary key
+ctx.Db.Task.Id.Delete(taskId);
+```
+
+### Index operations
+
+```csharp
+// Find by unique index — returns nullable
+if (ctx.Db.Player.Username.Find("alice") is Player player)
+{
+ // Found player
+}
+
+// Filter by B-tree index — returns iterator
+foreach (var task in ctx.Db.Task.OwnerId.Filter(ctx.Sender))
+{
+ // Process each task
+}
+```
+
+### Iterate all rows
+
+```csharp
+// Full table scan
+foreach (var task in ctx.Db.Task.Iter())
+{
+ // Process each task
+}
+```
+
+---
+
+## 5) Custom Types
+
+**Use `[SpacetimeDB.Type]` for custom structs/enums. Must be `partial`.**
+
+```csharp
+using SpacetimeDB;
+
+[SpacetimeDB.Type]
+public partial struct Position
+{
+ public int X;
+ public int Y;
+}
+
+[SpacetimeDB.Type]
+public partial struct PlayerStats
+{
+ public int Health;
+ public int Mana;
+ public Position Location;
+}
+
+// Use in table
+[SpacetimeDB.Table(Name = "player", Public = true)]
+public partial struct Player
+{
+ [SpacetimeDB.PrimaryKey]
+ public Identity Id;
+
+ public string Name;
+ public PlayerStats Stats;
+}
+```
+
+---
+
+## 6) Sum Types / Tagged Enums (CRITICAL)
+
+**Sum types MUST use `partial record` and inherit from `TaggedEnum`.**
+
+```csharp
+using SpacetimeDB;
+
+// Step 1: Define variant types as partial structs with [Type]
+[SpacetimeDB.Type]
+public partial struct Circle { public int Radius; }
+
+[SpacetimeDB.Type]
+public partial struct Rectangle { public int Width; public int Height; }
+
+// Step 2: Define sum type as partial RECORD (not struct!) inheriting TaggedEnum
+// The tuple MUST include both the type AND a name for each variant
+[SpacetimeDB.Type]
+public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }
+
+// Step 3: Use in a table
+[SpacetimeDB.Table(Name = "drawings", Public = true)]
+public partial struct Drawing
+{
+ [SpacetimeDB.PrimaryKey]
+ public int Id;
+ public Shape ShapeA;
+ public Shape ShapeB;
+}
+```
+
+### Creating sum type values
+
+```csharp
+// Create variant instances using the generated nested types
+var circle = new Shape.Circle(new Circle { Radius = 10 });
+var rect = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 });
+
+// Insert into table
+ctx.Db.Drawing.Insert(new Drawing { Id = 1, ShapeA = circle, ShapeB = rect });
+```
+
+### COMMON SUM TYPE MISTAKES
+
+| Wrong | Right | Why |
+|-------|-------|-----|
+| `partial struct Shape : TaggedEnum<...>` | `partial record Shape : TaggedEnum<...>` | Must be `record`, not `struct` |
+| `TaggedEnum<(Circle, Rectangle)>` | `TaggedEnum<(Circle Circle, Rectangle Rectangle)>` | Tuple must have names |
+| `new Shape { ... }` | `new Shape.Circle(new Circle { ... })` | Use nested variant constructor |
+
+---
+
+## 8) Scheduled Tables
+
+```csharp
+using SpacetimeDB;
+
+[SpacetimeDB.Table(Name = "reminder", Scheduled = nameof(Module.SendReminder))]
+public partial struct Reminder
+{
+ [SpacetimeDB.PrimaryKey]
+ [SpacetimeDB.AutoInc]
+ public ulong Id;
+
+ public string Message;
+ public ScheduleAt ScheduledAt;
+}
+
+public static partial class Module
+{
+ // Scheduled reducer receives the full row
+ [SpacetimeDB.Reducer]
+ public static void SendReminder(ReducerContext ctx, Reminder reminder)
+ {
+ Log.Info($"Reminder: {reminder.Message}");
+ // Row is automatically deleted after reducer completes
+ }
+
+ [SpacetimeDB.Reducer]
+ public static void CreateReminder(ReducerContext ctx, string message, ulong delaySecs)
+ {
+ var futureTime = ctx.Timestamp + TimeSpan.FromSeconds(delaySecs);
+ ctx.Db.Reminder.Insert(new Reminder
+ {
+ Id = 0,
+ Message = message,
+ ScheduledAt = ScheduleAt.Time(futureTime)
+ });
+ }
+
+ [SpacetimeDB.Reducer]
+ public static void CancelReminder(ReducerContext ctx, ulong reminderId)
+ {
+ ctx.Db.Reminder.Id.Delete(reminderId);
+ }
+}
+```
+
+---
+
+## 9) Logging
+
+```csharp
+using SpacetimeDB;
+
+Log.Debug("Debug message");
+Log.Info("Information");
+Log.Warn("Warning");
+Log.Error("Error occurred");
+Log.Panic("Critical failure"); // Terminates execution
+```
+
+---
+
+## 10) Data Visibility
+
+**`Public = true` exposes ALL rows to ALL clients.**
+
+| Scenario | Pattern |
+|----------|---------|
+| Everyone sees all rows | `[Table(Name = "x", Public = true)]` |
+| Server-only data | `[Table(Name = "x")]` (private by default) |
+
+---
+
+## 11) Project Setup
+
+### Required .csproj (MUST be named `StdbModule.csproj`)
+
+```xml
+
+
+ net8.0
+ wasi-wasm
+ Exe
+ enable
+ enable
+
+
+
+
+
+```
+
+### Prerequisites
+
+```bash
+# Install .NET 8 SDK (required, not .NET 9)
+# Download from https://dotnet.microsoft.com/download/dotnet/8.0
+
+# Install WASI workload
+dotnet workload install wasi-experimental
+```
+
+---
+
+## 12) Commands
+
+```bash
+# Start local server
+spacetime start
+
+# Publish module
+spacetime publish --project-path
+
+# Clear database and republish
+spacetime publish --clear-database -y --project-path
+
+# Generate bindings
+spacetime generate --lang csharp --out-dir /SpacetimeDB --project-path
+
+# View logs
+spacetime logs
+```
+
+---
+
+## 13) Hard Requirements
+
+1. **Tables and Module MUST be `partial`** — required for code generation
+2. **Use PascalCase for table access** — `ctx.Db.TableName`, not `ctx.Db.tableName`
+3. **Project file MUST be named `StdbModule.csproj`** — CLI requirement
+4. **Requires .NET 8 SDK** — .NET 9 and newer not yet supported
+5. **Install WASI workload** — `dotnet workload install wasi-experimental`
+6. **C# does NOT support procedures** — use reducers only
+7. **Reducers must be deterministic** — no filesystem, network, timers, or `Random`
+8. **Add `Public = true`** — if clients need to subscribe to a table
+9. **Use `T?` for nullable fields** — not `Optional`
+10. **Pass `0` for auto-increment** — to trigger ID generation on insert
diff --git a/docs/static/ai-rules/spacetimedb-rust.mdc b/docs/static/ai-rules/spacetimedb-rust.mdc
new file mode 100644
index 00000000000..0c242265ace
--- /dev/null
+++ b/docs/static/ai-rules/spacetimedb-rust.mdc
@@ -0,0 +1,530 @@
+---
+description: "MANDATORY: Read this ENTIRE file before writing ANY SpacetimeDB Rust code. Contains critical SDK patterns and HALLUCINATED APIs to avoid."
+globs: **/*.rs
+alwaysApply: true
+---
+
+# SpacetimeDB Rust SDK
+
+> **Tested with:** SpacetimeDB runtime 1.11.x, `spacetimedb` crate 1.1.x
+> **Last updated:** 2026-01-14
+
+---
+
+## HALLUCINATED APIs — DO NOT USE
+
+**These APIs DO NOT EXIST. LLMs frequently hallucinate them.**
+
+```rust
+// WRONG — these macros/attributes don't exist
+#[spacetimedb::table] // Use #[table] after importing
+#[spacetimedb::reducer] // Use #[reducer] after importing
+#[derive(Table)] // Tables use #[table] attribute, not derive
+#[derive(Reducer)] // Reducers use #[reducer] attribute
+
+// WRONG — SpacetimeType on tables
+#[derive(SpacetimeType)] // DO NOT use on #[table] structs!
+#[table(name = my_table)]
+pub struct MyTable { ... }
+
+// WRONG — mutable context
+pub fn my_reducer(ctx: &mut ReducerContext, ...) { } // Should be &ReducerContext
+
+// WRONG — table access without parentheses
+ctx.db.player // Should be ctx.db.player()
+ctx.db.player.find(id) // Should be ctx.db.player().id().find(&id)
+```
+
+### CORRECT PATTERNS:
+
+```rust
+// CORRECT IMPORTS
+use spacetimedb::{table, reducer, Table, ReducerContext, Identity, Timestamp};
+use spacetimedb::SpacetimeType; // Only for custom types, NOT tables
+
+// CORRECT TABLE — no SpacetimeType derive!
+#[table(name = player, public)]
+pub struct Player {
+ #[primary_key]
+ pub id: u64,
+ pub name: String,
+}
+
+// CORRECT REDUCER — immutable context reference
+#[reducer]
+pub fn create_player(ctx: &ReducerContext, name: String) {
+ ctx.db.player().insert(Player { id: 0, name });
+}
+
+// CORRECT TABLE ACCESS — methods with parentheses
+let player = ctx.db.player().id().find(&player_id);
+```
+
+### DO NOT:
+- **Derive `SpacetimeType` on `#[table]` structs** — the macro handles this
+- **Use mutable context** — `&ReducerContext`, not `&mut ReducerContext`
+- **Forget `Table` trait import** — required for table operations
+- **Use field access for tables** — `ctx.db.player()` not `ctx.db.player`
+
+---
+
+## 1) Common Mistakes Table
+
+### Server-side errors
+
+| Wrong | Right | Error |
+|-------|-------|-------|
+| `#[derive(SpacetimeType)]` on `#[table]` | Remove it — macro handles this | Conflicting derive macros |
+| `ctx.db.player` (field access) | `ctx.db.player()` (method) | "no field `player` on type" |
+| `ctx.db.player().find(id)` | `ctx.db.player().id().find(&id)` | Must access via index |
+| `&mut ReducerContext` | `&ReducerContext` | Wrong context type |
+| Missing `use spacetimedb::Table;` | Add import | "no method named `insert`" |
+| `#[table(name = "my_table")]` | `#[table(name = my_table)]` | String literals not allowed |
+| Missing `public` on table | Add `public` flag | Clients can't subscribe |
+| `#[spacetimedb::reducer]` | `#[reducer]` after import | Wrong attribute path |
+| Network/filesystem in reducer | Use procedures instead | Sandbox violation |
+| Panic for expected errors | Return `Result<(), String>` | WASM instance destroyed |
+
+### Client-side errors
+
+| Wrong | Right | Error |
+|-------|-------|-------|
+| Wrong crate name | `spacetimedb-sdk` | Dependency not found |
+| Manual event loop | Use `tokio` runtime | Async issues |
+
+---
+
+## 2) Table Definition (CRITICAL)
+
+**Tables use the `#[table]` attribute macro, NOT `#[derive(SpacetimeType)]`**
+
+```rust
+use spacetimedb::{table, Table, Identity, Timestamp};
+
+// WRONG — DO NOT derive SpacetimeType on tables!
+#[derive(SpacetimeType)] // REMOVE THIS!
+#[table(name = task)]
+pub struct Task { ... }
+
+// RIGHT — just the #[table] attribute
+#[table(name = task, public)]
+pub struct Task {
+ #[primary_key]
+ #[auto_inc]
+ pub id: u64,
+
+ pub owner_id: Identity,
+ pub title: String,
+ pub created_at: Timestamp,
+}
+
+// With indexes
+#[table(name = task, public, index(name = by_owner, btree(columns = [owner_id])))]
+pub struct Task {
+ #[primary_key]
+ #[auto_inc]
+ pub id: u64,
+
+ pub owner_id: Identity,
+ pub title: String,
+}
+```
+
+### Field attributes
+
+```rust
+#[primary_key] // Exactly one per table (required)
+#[auto_inc] // Auto-increment (integer primary keys only)
+#[unique] // Unique constraint (can have multiple)
+#[index(btree)] // Single-column BTree index
+```
+
+### Column types
+
+```rust
+u8, u16, u32, u64, u128 // Unsigned integers
+i8, i16, i32, i64, i128 // Signed integers
+f32, f64 // Floats
+bool // Boolean
+String // Text
+Identity // User identity
+Timestamp // Timestamp
+ScheduleAt // For scheduled tables
+Option // Nullable
+Vec // Arrays
+```
+
+### Insert returns the row
+
+```rust
+// Insert and get the auto-generated ID
+let row = ctx.db.task().insert(Task {
+ id: 0, // Placeholder for auto_inc
+ owner_id: ctx.sender,
+ title: "New task".to_string(),
+ created_at: ctx.timestamp,
+});
+let new_id = row.id; // Get the actual ID
+```
+
+---
+
+## 3) Index Access
+
+### Naming convention
+- **Tables**: snake_case methods on `ctx.db`
+ - `#[table(name = my_table)]` → `ctx.db.my_table()`
+- **Indexes**: exact declared name
+ - `index(name = by_owner, ...)` → `ctx.db.my_table().by_owner()`
+
+### Primary key operations
+
+```rust
+// Find by primary key — returns Option
+if let Some(task) = ctx.db.task().id().find(&task_id) {
+ // Use task
+}
+
+// Update by primary key
+ctx.db.task().id().update(Task { id: task_id, ...updated_fields });
+
+// Delete by primary key
+ctx.db.task().id().delete(&task_id);
+```
+
+### Index filter
+
+```rust
+// Filter by indexed column — returns iterator
+for task in ctx.db.task().by_owner().filter(&owner_id) {
+ // Process each task
+}
+```
+
+### Unique column lookup
+
+```rust
+// Find by unique column — returns Option
+if let Some(player) = ctx.db.player().username().find(&"alice".to_string()) {
+ // Found player
+}
+```
+
+### Iterate all rows
+
+```rust
+// Full table scan
+for task in ctx.db.task().iter() {
+ // Process each task
+}
+```
+
+---
+
+## 4) Reducers
+
+### Definition syntax
+
+```rust
+use spacetimedb::{reducer, ReducerContext, Table};
+
+#[reducer]
+pub fn create_task(ctx: &ReducerContext, title: String) {
+ // Validate
+ if title.is_empty() {
+ panic!("Title cannot be empty"); // Rolls back transaction
+ }
+
+ // Insert
+ ctx.db.task().insert(Task {
+ id: 0,
+ owner_id: ctx.sender,
+ title,
+ created_at: ctx.timestamp,
+ });
+}
+
+// With Result return type (preferred for recoverable errors)
+#[reducer]
+pub fn update_task(ctx: &ReducerContext, task_id: u64, title: String) -> Result<(), String> {
+ let task = ctx.db.task().id().find(&task_id)
+ .ok_or("Task not found")?;
+
+ if task.owner_id != ctx.sender {
+ return Err("Not authorized".to_string());
+ }
+
+ ctx.db.task().id().update(Task { title, ..task });
+ Ok(())
+}
+```
+
+### Lifecycle reducers
+
+```rust
+#[reducer(init)]
+pub fn init(ctx: &ReducerContext) {
+ // Called when module is first published
+}
+
+#[reducer(client_connected)]
+pub fn on_connect(ctx: &ReducerContext) {
+ // ctx.sender is the connecting client
+ log::info!("Client connected: {:?}", ctx.sender);
+}
+
+#[reducer(client_disconnected)]
+pub fn on_disconnect(ctx: &ReducerContext) {
+ // Clean up client state
+}
+```
+
+### ReducerContext fields
+
+```rust
+ctx.sender // Identity of the caller
+ctx.timestamp // Current timestamp
+ctx.db // Database access
+ctx.rng // Deterministic RNG (use instead of rand)
+```
+
+### Error handling
+
+```rust
+// Option 1: Panic (simple, destroys WASM instance)
+if condition_failed {
+ panic!("Error message");
+}
+
+// Option 2: Result (preferred, graceful error handling)
+#[reducer]
+pub fn my_reducer(ctx: &ReducerContext) -> Result<(), String> {
+ do_something().map_err(|e| e.to_string())?;
+ Ok(())
+}
+```
+
+---
+
+## 5) Custom Types
+
+**Use `#[derive(SpacetimeType)]` ONLY for custom structs/enums used as fields or parameters.**
+
+```rust
+use spacetimedb::SpacetimeType;
+
+// Custom struct for table fields
+#[derive(SpacetimeType, Clone, Debug, PartialEq)]
+pub struct Position {
+ pub x: i32,
+ pub y: i32,
+}
+
+// Custom enum
+#[derive(SpacetimeType, Clone, Debug, PartialEq)]
+pub enum PlayerStatus {
+ Idle,
+ Walking(Position),
+ Fighting(Identity),
+}
+
+// Use in table
+#[table(name = player, public)]
+pub struct Player {
+ #[primary_key]
+ pub id: Identity,
+ pub position: Position,
+ pub status: PlayerStatus,
+}
+```
+
+---
+
+## 6) Scheduled Tables
+
+```rust
+use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};
+
+#[table(name = reminder, scheduled(send_reminder))]
+pub struct Reminder {
+ #[primary_key]
+ #[auto_inc]
+ pub id: u64,
+ pub message: String,
+ pub scheduled_at: ScheduleAt,
+}
+
+// Scheduled reducer receives the full row
+#[reducer]
+fn send_reminder(ctx: &ReducerContext, reminder: Reminder) {
+ log::info!("Reminder: {}", reminder.message);
+ // Row is automatically deleted after reducer completes
+}
+
+// Schedule a reminder
+#[reducer]
+pub fn create_reminder(ctx: &ReducerContext, message: String, delay_secs: u64) {
+ let future_time = ctx.timestamp + std::time::Duration::from_secs(delay_secs);
+ ctx.db.reminder().insert(Reminder {
+ id: 0,
+ message,
+ scheduled_at: ScheduleAt::Time(future_time),
+ });
+}
+
+// Cancel by deleting the row
+#[reducer]
+pub fn cancel_reminder(ctx: &ReducerContext, reminder_id: u64) {
+ ctx.db.reminder().id().delete(&reminder_id);
+}
+```
+
+---
+
+## 7) Timestamps
+
+```rust
+use spacetimedb::Timestamp;
+
+// Current time from context
+let now = ctx.timestamp;
+
+// Create future timestamp
+let future = ctx.timestamp + std::time::Duration::from_secs(60);
+
+// Compare timestamps
+if row.created_at < ctx.timestamp {
+ // Row was created before now
+}
+```
+
+---
+
+## 8) Data Visibility
+
+**`public` flag exposes ALL rows to ALL clients.**
+
+| Scenario | Pattern |
+|----------|---------|
+| Everyone sees all rows | `#[table(name = x, public)]` |
+| Users see only their data | Private table + row-level security |
+
+### Private table (default)
+
+```rust
+// No public flag — only server can read
+#[table(name = secret_data)]
+pub struct SecretData { ... }
+```
+
+### Row-level security
+
+```rust
+// Use row-level security for per-user visibility
+#[table(name = player_data, public)]
+#[rls(filter = |ctx, row| row.owner_id == ctx.sender)]
+pub struct PlayerData {
+ #[primary_key]
+ pub id: u64,
+ pub owner_id: Identity,
+ pub data: String,
+}
+```
+
+---
+
+## 9) Procedures (Beta)
+
+**Procedures are for side effects (HTTP, filesystem) that reducers can't do.**
+
+Procedures are currently unstable. Enable with:
+
+```toml
+# Cargo.toml
+[dependencies]
+spacetimedb = { version = "1.*", features = ["unstable"] }
+```
+
+```rust
+use spacetimedb::{procedure, ProcedureContext};
+
+// Simple procedure
+#[procedure]
+fn add_numbers(_ctx: &mut ProcedureContext, a: u32, b: u32) -> u64 {
+ a as u64 + b as u64
+}
+
+// Procedure with database access
+#[procedure]
+fn save_external_data(ctx: &mut ProcedureContext, url: String) -> Result<(), String> {
+ // HTTP request (allowed in procedures, not reducers)
+ let data = fetch_from_url(&url)?;
+
+ // Database access requires explicit transaction
+ ctx.try_with_tx(|tx| {
+ tx.db.external_data().insert(ExternalData {
+ id: 0,
+ content: data,
+ });
+ Ok(())
+ })?;
+
+ Ok(())
+}
+```
+
+### Key differences from reducers
+
+| Reducers | Procedures |
+|----------|------------|
+| `&ReducerContext` (immutable) | `&mut ProcedureContext` (mutable) |
+| Direct `ctx.db` access | Must use `ctx.with_tx()` |
+| No HTTP/network | HTTP allowed |
+| No return values | Can return data |
+
+---
+
+## 10) Logging
+
+```rust
+use spacetimedb::log;
+
+log::trace!("Detailed trace");
+log::debug!("Debug info");
+log::info!("Information");
+log::warn!("Warning");
+log::error!("Error occurred");
+```
+
+---
+
+## 11) Commands
+
+```bash
+# Start local server
+spacetime start
+
+# Publish module
+spacetime publish --project-path
+
+# Clear database and republish
+spacetime publish --clear-database -y --project-path
+
+# Generate bindings
+spacetime generate --lang rust --out-dir /src/module_bindings --project-path
+
+# View logs
+spacetime logs
+```
+
+---
+
+## 12) Hard Requirements
+
+1. **DO NOT derive `SpacetimeType` on `#[table]` structs** — the macro handles this
+2. **Import `Table` trait** — required for all table operations
+3. **Use `&ReducerContext`** — not `&mut ReducerContext`
+4. **Tables are methods** — `ctx.db.table()` not `ctx.db.table`
+5. **Reducers must be deterministic** — no filesystem, network, timers, or external RNG
+6. **Use `ctx.rng`** — not `rand` crate for random numbers
+7. **Add `public` flag** — if clients need to subscribe to a table
diff --git a/docs/static/ai-rules/spacetimedb-typescript.mdc b/docs/static/ai-rules/spacetimedb-typescript.mdc
index 724aaf34d77..a218ddc844c 100644
--- a/docs/static/ai-rules/spacetimedb-typescript.mdc
+++ b/docs/static/ai-rules/spacetimedb-typescript.mdc
@@ -67,6 +67,9 @@ const [items, isLoading] = useTable(tables.item);
| `.unique()` + explicit index | Just use `.unique()` | "name is used for multiple entities" |
| Import spacetimedb from index.ts | Import from schema.ts | "Cannot access before initialization" |
| Multi-column index `.filter()` | **⚠️ BROKEN** — use single-column | PANIC or silent empty results |
+| `.iter()` in views | Use index lookups only | Views can't scan tables |
+| `ctx.db` in procedures | `ctx.withTx(tx => tx.db...)` | Procedures need explicit transactions |
+| `ctx.myTable` in procedure tx | `tx.db.myTable` | Wrong context variable |
### Client-side errors
@@ -303,22 +306,50 @@ if (scheduleAt.tag === 'Time') {
| Users see only their data | Private table + public view |
### Private table + view pattern
+
+⚠️ **CRITICAL: Views can ONLY access data via index lookups, NOT `.iter()`**
+
```typescript
-// Private table (no public: true)
+// Private table with index on ownerId
export const PrivateData = table(
- { name: 'private_data' },
- { id: t.u64().primaryKey().autoInc(), ownerId: t.identity(), secret: t.string() }
+ { name: 'private_data',
+ indexes: [{ name: 'by_owner', algorithm: 'btree', columns: ['ownerId'] }]
+ },
+ {
+ id: t.u64().primaryKey().autoInc(),
+ ownerId: t.identity(),
+ secret: t.string()
+ }
+);
+
+// ❌ WRONG — views cannot use .iter()
+spacetimedb.view(
+ { name: 'my_data_wrong', public: true },
+ t.array(PrivateData.rowType),
+ (ctx) => [...ctx.db.privateData.iter()] // ❌ NOT ALLOWED
);
-// Public view filtered by sender
+// ✅ RIGHT — use index lookup
spacetimedb.view(
{ name: 'my_data', public: true },
- t.array(t.object('MyDataRow', { id: t.u64(), secret: t.string() })),
- (ctx) => [...ctx.db.privateData.iter()]
- .filter(row => row.ownerId.toHexString() === ctx.sender.toHexString())
+ t.array(PrivateData.rowType),
+ (ctx) => [...ctx.db.privateData.by_owner.filter(ctx.sender)]
);
```
+### ViewContext vs AnonymousViewContext
+```typescript
+// ViewContext — has ctx.sender, result varies per user (computed per-subscriber)
+spacetimedb.view({ name: 'my_items', public: true }, t.array(Item.rowType), (ctx) => {
+ return [...ctx.db.item.by_owner.filter(ctx.sender)];
+});
+
+// AnonymousViewContext — no ctx.sender, same result for everyone (shared, better perf)
+spacetimedb.anonymousView({ name: 'leaderboard', public: true }, t.array(LeaderboardRow), (ctx) => {
+ return [...ctx.db.player.by_score.filter(/* top scores */)];
+});
+```
+
**Views require explicit subscription:**
```typescript
conn.subscriptionBuilder().subscribe([
@@ -408,7 +439,63 @@ src/config.ts → MODULE_NAME, SPACETIMEDB_URI
---
-## 10) Commands
+## 10) Procedures (Beta)
+
+**Procedures are for side effects (HTTP requests, etc.) that reducers can't do.**
+
+⚠️ Procedures are currently in beta. API may change.
+
+### Defining a procedure
+```typescript
+spacetimedb.procedure(
+ 'fetch_external_data',
+ { url: t.string() },
+ t.string(), // return type
+ (ctx, { url }) => {
+ const response = ctx.http.fetch(url);
+ return response.text();
+ }
+);
+```
+
+### Database access in procedures
+
+⚠️ **CRITICAL: Procedures don't have `ctx.db`. Use `ctx.withTx()` for database access.**
+
+```typescript
+spacetimedb.procedure('save_fetched_data', { url: t.string() }, t.unit(), (ctx, { url }) => {
+ // Fetch external data (outside transaction)
+ const response = ctx.http.fetch(url);
+ const data = response.text();
+
+ // ❌ WRONG — ctx.db doesn't exist in procedures
+ ctx.db.myTable.insert({ ... });
+
+ // ✅ RIGHT — use ctx.withTx() for database access
+ ctx.withTx(tx => {
+ tx.db.myTable.insert({
+ id: 0n,
+ content: data,
+ fetchedAt: tx.timestamp,
+ fetchedBy: tx.sender,
+ });
+ });
+
+ return {};
+});
+```
+
+### Key differences from reducers
+| Reducers | Procedures |
+|----------|------------|
+| `ctx.db` available directly | Must use `ctx.withTx(tx => tx.db...)` |
+| Automatic transaction | Manual transaction management |
+| No HTTP/network | `ctx.http.fetch()` available |
+| No return values to caller | Can return data to caller |
+
+---
+
+## 11) Commands
```bash
# Start local server
@@ -429,7 +516,7 @@ spacetime logs
---
-## 11) Hard Requirements
+## 12) Hard Requirements
1. **DO NOT edit generated bindings** — regenerate with `spacetime generate`
2. **Reducers are transactional** — they do not return data
diff --git a/docs/static/ai-rules/spacetimedb.mdc b/docs/static/ai-rules/spacetimedb.mdc
index c2457404f60..7ccf1549feb 100644
--- a/docs/static/ai-rules/spacetimedb.mdc
+++ b/docs/static/ai-rules/spacetimedb.mdc
@@ -12,8 +12,8 @@ alwaysApply: true
| Language | Rule File |
|----------|-----------|
| **TypeScript/React** | `spacetimedb-typescript.mdc` (MANDATORY) |
-| **Rust** | Check existing Rust modules in repo |
-| **C#** | Check existing C# modules in repo |
+| **Rust** | `spacetimedb-rust.mdc` (MANDATORY) |
+| **C#** | `spacetimedb-csharp.mdc` (MANDATORY) |
---
diff --git a/docs/static/llms.md b/docs/static/llms.md
index a4c1a577f58..876a91b31ad 100644
--- a/docs/static/llms.md
+++ b/docs/static/llms.md
@@ -57,6 +57,65 @@ Clockwork Labs, the developers of SpacetimeDB, offers three products:
2. SpacetimeDB Maincloud: a hosted, managed-service, serverless cluster
3. SpacetimeDB Enterprise: a closed-source, clusterized version of SpacetimeDB which can be licensed for on-prem hosting or dedicated hosting
+## Documentation Directory
+
+### Getting Started
+- [What is SpacetimeDB](/intro/what-is-spacetimedb) - Overview and core concepts
+- [Key Architecture](/intro/key-architecture) - How SpacetimeDB works
+- [Language Support](/intro/language-support) - Supported languages and SDKs
+- [FAQ](/intro/faq) - Frequently asked questions
+
+### Quickstarts
+- [React Quickstart](/quickstarts/react) - Get started with React + TypeScript
+- [TypeScript Quickstart](/quickstarts/typescript) - TypeScript server module
+- [Rust Quickstart](/quickstarts/rust) - Rust server module
+- [C# Quickstart](/quickstarts/c-sharp) - C# server module
+
+### Core Concepts
+- [Databases](/databases) - Database modules overview
+- [Tables](/tables) - Defining and working with tables
+ - [Columns](/tables/columns) - Column types and definitions
+ - [Indexes](/tables/indexes) - Creating and using indexes
+ - [Scheduled Tables](/tables/scheduled-tables) - Time-based scheduling
+ - [Access Permissions](/tables/access-permissions) - Table visibility (public/private)
+- [Functions](/functions) - Server-side logic
+ - [Reducers](/functions/reducers) - Transactional RPC functions
+ - [Reducer Context](/functions/reducers/reducer-context) - ctx.db, ctx.sender, etc.
+ - [Lifecycle Reducers](/functions/reducers/lifecycle) - init, client_connected, client_disconnected
+ - [Error Handling](/functions/reducers/error-handling) - Handling errors in reducers
+ - [Procedures](/functions/procedures) - Non-transactional functions with side effects
+ - [Views](/functions/views) - Computed data views
+- [Subscriptions](/subscriptions) - Real-time data synchronization
+ - [Subscription Semantics](/subscriptions/semantics) - How subscriptions work
+- [Client SDKs](/sdks) - Client-side integration
+ - [Code Generation](/sdks/codegen) - Generating type-safe bindings
+
+### Development
+- [spacetime dev](/databases/developing) - Interactive development mode
+- [Building & Publishing](/databases/building-publishing) - Deploying modules
+- [Cheat Sheet](/databases/cheat-sheet) - Quick reference for common operations
+- [Automatic Migrations](/databases/automatic-migrations) - Schema migration handling
+
+### Deployment
+- [Deploy to Maincloud](/how-to/deploy/maincloud) - Hosted deployment
+- [Self-Hosting](/how-to/deploy/self-hosting) - Run your own server
+
+### Reference
+- [CLI Reference](/cli-reference) - Command-line tool documentation
+- [SQL Reference](/reference/sql) - SQL query syntax
+- [HTTP API](/http/database) - REST API reference
+
+### AI Assistant Rules
+
+**IMPORTANT:** Before writing SpacetimeDB code, consult the language-specific rules files. These contain critical information about hallucinated APIs, common mistakes, and correct patterns:
+
+| Language | Rules |
+|----------|-------|
+| All Languages | [spacetimedb.mdc](https://spacetimedb.com/ai-rules/spacetimedb.mdc) |
+| TypeScript | [spacetimedb-typescript.mdc](https://spacetimedb.com/ai-rules/spacetimedb-typescript.mdc) |
+| Rust | [spacetimedb-rust.mdc](https://spacetimedb.com/ai-rules/spacetimedb-rust.mdc) |
+| C# | [spacetimedb-csharp.mdc](https://spacetimedb.com/ai-rules/spacetimedb-csharp.mdc) |
+
## Basic Project Workflow
Getting started with SpacetimeDB involves a few key steps:
diff --git a/templates/basic-c-sharp/spacetimedb/Lib.cs b/templates/basic-c-sharp/spacetimedb/Lib.cs
index 98a85f15a4d..0e24e2496e9 100644
--- a/templates/basic-c-sharp/spacetimedb/Lib.cs
+++ b/templates/basic-c-sharp/spacetimedb/Lib.cs
@@ -2,21 +2,16 @@
public static partial class Module
{
- [SpacetimeDB.Table]
+ [SpacetimeDB.Table(Name = "Person", Public = true)]
public partial struct Person
{
- [SpacetimeDB.AutoInc]
- [SpacetimeDB.PrimaryKey]
- public int Id;
public string Name;
- public int Age;
}
[SpacetimeDB.Reducer]
- public static void Add(ReducerContext ctx, string name, int age)
+ public static void Add(ReducerContext ctx, string name)
{
- var person = ctx.Db.Person.Insert(new Person { Name = name, Age = age });
- Log.Info($"Inserted {person.Name} under #{person.Id}");
+ ctx.Db.Person.Insert(new Person { Name = name });
}
[SpacetimeDB.Reducer]
diff --git a/templates/quickstart-chat-c-sharp/spacetimedb/Lib.cs b/templates/quickstart-chat-c-sharp/spacetimedb/Lib.cs
index 79a56520cd2..3d82175859a 100644
--- a/templates/quickstart-chat-c-sharp/spacetimedb/Lib.cs
+++ b/templates/quickstart-chat-c-sharp/spacetimedb/Lib.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "user", Public = true)]
+ [Table(Name = "User", Public = true)]
public partial class User
{
[PrimaryKey]
@@ -11,7 +11,7 @@ public partial class User
public bool Online;
}
- [Table(Name = "message", Public = true)]
+ [Table(Name = "Message", Public = true)]
public partial class Message
{
public Identity Sender;
@@ -24,10 +24,10 @@ public static void SetName(ReducerContext ctx, string name)
{
name = ValidateName(name);
- if (ctx.Db.user.Identity.Find(ctx.Sender) is User user)
+ if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
{
user.Name = name;
- ctx.Db.user.Identity.Update(user);
+ ctx.Db.User.Identity.Update(user);
}
}
@@ -46,7 +46,7 @@ public static void SendMessage(ReducerContext ctx, string text)
{
text = ValidateMessage(text);
Log.Info(text);
- ctx.Db.message.Insert(
+ ctx.Db.Message.Insert(
new Message
{
Sender = ctx.Sender,
@@ -71,18 +71,18 @@ public static void ClientConnected(ReducerContext ctx)
{
Log.Info($"Connect {ctx.Sender}");
- if (ctx.Db.user.Identity.Find(ctx.Sender) is User user)
+ if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
{
// If this is a returning user, i.e., we already have a `User` with this `Identity`,
// set `Online: true`, but leave `Name` and `Identity` unchanged.
user.Online = true;
- ctx.Db.user.Identity.Update(user);
+ ctx.Db.User.Identity.Update(user);
}
else
{
// If this is a new user, create a `User` object for the `Identity`,
// which is online, but hasn't set a name.
- ctx.Db.user.Insert(
+ ctx.Db.User.Insert(
new User
{
Name = null,
@@ -96,11 +96,11 @@ public static void ClientConnected(ReducerContext ctx)
[Reducer(ReducerKind.ClientDisconnected)]
public static void ClientDisconnected(ReducerContext ctx)
{
- if (ctx.Db.user.Identity.Find(ctx.Sender) is User user)
+ if (ctx.Db.User.Identity.Find(ctx.Sender) is User user)
{
// This user should exist, so set `Online: false`.
user.Online = false;
- ctx.Db.user.Identity.Update(user);
+ ctx.Db.User.Identity.Update(user);
}
else
{
diff --git a/tools/xtask-llm-benchmark/Cargo.toml b/tools/xtask-llm-benchmark/Cargo.toml
index 7154322678a..fed07f2f06c 100644
--- a/tools/xtask-llm-benchmark/Cargo.toml
+++ b/tools/xtask-llm-benchmark/Cargo.toml
@@ -23,6 +23,7 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
urlencoding = "2.1.3"
reqwest = { version = "0.12", features = ["json"] }
futures = "0.3.31"
+regex = "1"
tempfile = "3.23.0"
fs2 = "0.4.3"
heck = "0.5.0"
diff --git a/tools/xtask-llm-benchmark/src/bench/publishers.rs b/tools/xtask-llm-benchmark/src/bench/publishers.rs
index a85556fe6ce..2b9707a6035 100644
--- a/tools/xtask-llm-benchmark/src/bench/publishers.rs
+++ b/tools/xtask-llm-benchmark/src/bench/publishers.rs
@@ -1,8 +1,20 @@
use crate::bench::utils::sanitize_db_name;
use anyhow::{bail, Result};
+use regex::Regex;
+use std::borrow::Cow;
use std::fs;
use std::path::Path;
use std::process::Command;
+use std::sync::LazyLock;
+
+/// Strip ANSI escape codes (color codes) from a string
+fn strip_ansi_codes(s: &str) -> Cow<'_, str> {
+ static ANSI_RE: LazyLock = LazyLock::new(|| {
+ // Matches ANSI escape sequences like \x1b[31m, \x1b[0m, etc.
+ Regex::new(r"\x1b\[[0-9;]*m").unwrap()
+ });
+ ANSI_RE.replace_all(s, "")
+}
/* -------------------------------------------------------------------------- */
/* Shared */
@@ -66,8 +78,10 @@ fn run_with_retry(cmd: &mut Command, label: &str, max_retries: u32) -> Result<()
}
let code = out.status.code().unwrap_or(-1);
- let stderr = String::from_utf8_lossy(&out.stderr);
- let stdout = String::from_utf8_lossy(&out.stdout);
+ let stderr_raw = String::from_utf8_lossy(&out.stderr);
+ let stdout_raw = String::from_utf8_lossy(&out.stdout);
+ let stderr = strip_ansi_codes(&stderr_raw);
+ let stdout = strip_ansi_codes(&stdout_raw);
// Retry on signal kills (like SIGSEGV) or transient build errors
let should_retry = was_signal_killed(&out.status) || is_transient_build_error(&stderr, &stdout);
@@ -129,7 +143,11 @@ impl Publisher for DotnetPublisher {
cmd.arg("build")
.current_dir(source)
.env("DOTNET_CLI_TELEMETRY_OPTOUT", "1")
- .env("DOTNET_NOLOGO", "1");
+ .env("DOTNET_NOLOGO", "1")
+ // Prevent MSBuild node reuse issues that cause "Pipe is broken" errors
+ // when running multiple dotnet builds in parallel.
+ .env("MSBUILDDISABLENODEREUSE", "1")
+ .env("DOTNET_CLI_USE_MSBUILD_SERVER", "0");
run(&mut cmd, "spacetime build (csharp)")?;
let mut pubcmd = Command::new("spacetime");
diff --git a/tools/xtask-llm-benchmark/src/bench/runner.rs b/tools/xtask-llm-benchmark/src/bench/runner.rs
index 7df295eaac9..d2aacd5f34f 100644
--- a/tools/xtask-llm-benchmark/src/bench/runner.rs
+++ b/tools/xtask-llm-benchmark/src/bench/runner.rs
@@ -177,13 +177,10 @@ impl TaskRunner {
let prompt = prompt_builder.build_segmented(cfg.context);
println!("→ [{}] {}: calling provider", cfg.lang_name, cfg.route.display_name);
- let llm_output = tokio::time::timeout(
- std::time::Duration::from_secs(200),
- cfg.llm.generate(cfg.route, &prompt),
- )
- .await
- .map_err(|_| RunOneError::Other(anyhow!("LLM call timed out")))?
- .map_err(RunOneError::Other)?;
+ let llm_output = tokio::time::timeout(std::time::Duration::from_secs(90), cfg.llm.generate(cfg.route, &prompt))
+ .await
+ .map_err(|_| RunOneError::Other(anyhow!("LLM call timed out")))?
+ .map_err(RunOneError::Other)?;
if debug_llm() {
print_llm_output(cfg.route.display_name, &task_id, &llm_output);
diff --git a/tools/xtask-llm-benchmark/src/bench/utils.rs b/tools/xtask-llm-benchmark/src/bench/utils.rs
index f37bf4e9caa..df643d356e7 100644
--- a/tools/xtask-llm-benchmark/src/bench/utils.rs
+++ b/tools/xtask-llm-benchmark/src/bench/utils.rs
@@ -106,14 +106,14 @@ pub fn bench_csharp_concurrency() -> usize {
env::var("LLM_BENCH_CSHARP_CONCURRENCY")
.ok()
.and_then(|s| s.parse().ok())
- .unwrap_or(2)
+ .unwrap_or(4)
}
pub fn bench_route_concurrency() -> usize {
env::var("LLM_BENCH_ROUTE_CONCURRENCY")
.ok()
.and_then(|s| s.parse().ok())
- .unwrap_or(2)
+ .unwrap_or(4)
}
pub fn fmt_dur(d: Duration) -> String {
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/answers/csharp.cs
index f6887c99be2..804715fc867 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/answers/csharp.cs
@@ -2,8 +2,8 @@
public static partial class Module
{
- [Table(Name = "users")]
- public partial struct Users
+ [Table(Name = "User")]
+ public partial struct User
{
[PrimaryKey] public int Id;
public string Name;
@@ -11,8 +11,8 @@ public partial struct Users
public bool Active;
}
- [Table(Name = "products")]
- public partial struct Products
+ [Table(Name = "Product")]
+ public partial struct Product
{
[PrimaryKey] public int Id;
public string Title;
@@ -20,8 +20,8 @@ public partial struct Products
public bool InStock;
}
- [Table(Name = "notes")]
- public partial struct Notes
+ [Table(Name = "Note")]
+ public partial struct Note
{
[PrimaryKey] public int Id;
public string Body;
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/tasks/csharp.txt
index 263d0cb93f3..d5a095dd5d4 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_001_basic_tables/tasks/csharp.txt
@@ -1,24 +1,24 @@
Write a SpacetimeDB backend module in C# that defines three tables with basic columns.
TABLES
-- users
- - Struct: Users
+- User
+ - Struct: User
- Fields:
- Id: int (primary key)
- Name: string
- Age: int
- Active: bool
-- products
- - Struct: Products
+- Product
+ - Struct: Product
- Fields:
- Id: int (primary key)
- Title: string
- Price: float
- InStock: bool
-- notes
- - Struct: Notes
+- Note
+ - Struct: Note
- Fields:
- Id: int (primary key)
- Body: string
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/answers/csharp.cs
index 85d1f307f72..1341c3ff827 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "tick_timer", Scheduled = nameof(Tick), ScheduledAt = nameof(TickTimer.ScheduledAt))]
+ [Table(Name = "TickTimer", Scheduled = nameof(Tick), ScheduledAt = nameof(TickTimer.ScheduledAt))]
public partial struct TickTimer
{
[PrimaryKey, AutoInc] public ulong ScheduledId;
@@ -16,9 +16,9 @@ public static void Tick(ReducerContext ctx, TickTimer timer) { }
public static void Init(ReducerContext ctx)
{
var interval = new TimeDuration { Microseconds = 50_000 };
- ctx.Db.tick_timer.Insert(new TickTimer
+ ctx.Db.TickTimer.Insert(new TickTimer
{
ScheduledAt = new ScheduleAt.Interval(interval)
});
}
-}
\ No newline at end of file
+}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/tasks/csharp.txt
index 392980ab7ad..f5ae5ddb9e7 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_002_scheduled_table/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines a scheduled table and a scheduled reducer.
TABLE
-- tick_timer
+- TickTimer
- Struct: TickTimer
- Fields:
- ScheduledId: ulong (primary key, auto-increment)
@@ -11,5 +11,5 @@ TABLE
- Column: ScheduledAt
REDUCERS
-- Tick: scheduled reducer triggered by tick_timer
-- Init: insert exactly one row into tick_timer that schedules a repeating interval of 50_000 microseconds
+- Tick: scheduled reducer triggered by TickTimer
+- Init: insert exactly one row into TickTimer that schedules a repeating interval of 50_000 microseconds
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/answers/csharp.cs
index 012aeb0a8b5..ea202548977 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/answers/csharp.cs
@@ -9,7 +9,7 @@ public partial struct Position
public int Y;
}
- [Table(Name = "entities")]
+ [Table(Name = "Entity")]
public partial struct Entity
{
[PrimaryKey] public int Id;
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/tasks/csharp.txt
index f7ec62a8884..fe5fc6260b9 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_003_struct_in_table/tasks/csharp.txt
@@ -7,7 +7,7 @@ TYPES
- Y: int
TABLE
-- entities
+- Entity
- Struct: Entity
- Fields:
- Id: int (primary key)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/answers/csharp.cs
index 5f2cc7b0843..b6b05bf5c52 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int Id;
@@ -14,6 +14,6 @@ public partial struct User
[Reducer]
public static void InsertUser(ReducerContext ctx, int id, string name, int age, bool active)
{
- ctx.Db.users.Insert(new User { Id = id, Name = name, Age = age, Active = active });
+ ctx.Db.User.Insert(new User { Id = id, Name = name, Age = age, Active = active });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/tasks/csharp.txt
index a12196d3870..9990b6217ac 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_004_insert/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and a reducer that inserts a row.
TABLE
-- users
+- User
- Struct: User
- Fields:
- Id: int (primary key)
@@ -10,5 +10,5 @@ TABLE
- Active: bool
REDUCERS
-- InsertUser: given id:int, name:string, age:int, active:bool, insert exactly one row into users
+- InsertUser: given id:int, name:string, age:int, active:bool, insert exactly one row into User
- (Id=id, Name=name, Age=age, Active=active)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/answers/csharp.cs
index a6f72e8a5ec..d7fe82ee912 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int Id;
@@ -14,6 +14,6 @@ public partial struct User
[Reducer]
public static void UpdateUser(ReducerContext ctx, int id, string name, int age, bool active)
{
- ctx.Db.users.Id.Update(new User { Id = id, Name = name, Age = age, Active = active });
+ ctx.Db.User.Id.Update(new User { Id = id, Name = name, Age = age, Active = active });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/tasks/csharp.txt
index 3f4d33de5d6..1b5487adec5 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_005_update/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and a reducer that updates a row.
TABLE
-- users
+- User
- Struct: User
- Fields:
- Id: int (primary key)
@@ -10,5 +10,5 @@ TABLE
- Active: bool
REDUCERS
-- UpdateUser: given id:int, name:string, age:int, active:bool, update the row in users with Id=id to exactly these values
+- UpdateUser: given id:int, name:string, age:int, active:bool, update the row in User with Id=id to exactly these values
- (Id=id, Name=name, Age=age, Active=active)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/answers/csharp.cs
index e5bcd830d38..904afe54f3d 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int Id;
@@ -14,6 +14,6 @@ public partial struct User
[Reducer]
public static void DeleteUser(ReducerContext ctx, int id)
{
- ctx.Db.users.Id.Delete(id);
+ ctx.Db.User.Id.Delete(id);
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/tasks/csharp.txt
index 4154a1b905d..0e956841635 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_006_delete/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and a reducer that deletes a row.
TABLE
-- users
+- User
- Struct: User
- Fields:
- Id: int (primary key)
@@ -10,4 +10,4 @@ TABLE
- Active: bool
REDUCERS
-- DeleteUser: given id:int, delete the row in users with that Id
+- DeleteUser: given id:int, delete the row in User with that Id
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/answers/csharp.cs
index df12c117375..f2f84b5133d 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int Id;
@@ -14,9 +14,9 @@ public partial struct User
[Reducer]
public static void Crud(ReducerContext ctx)
{
- ctx.Db.users.Insert(new User { Id = 1, Name = "Alice", Age = 30, Active = true });
- ctx.Db.users.Insert(new User { Id = 2, Name = "Bob", Age = 22, Active = false });
- ctx.Db.users.Id.Update(new User { Id = 1, Name = "Alice2", Age = 31, Active = false });
- ctx.Db.users.Id.Delete(2);
+ ctx.Db.User.Insert(new User { Id = 1, Name = "Alice", Age = 30, Active = true });
+ ctx.Db.User.Insert(new User { Id = 2, Name = "Bob", Age = 22, Active = false });
+ ctx.Db.User.Id.Update(new User { Id = 1, Name = "Alice2", Age = 31, Active = false });
+ ctx.Db.User.Id.Delete(2);
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/tasks/csharp.txt
index 99e2d0024f5..540bdcee867 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_007_crud/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and a reducer that performs insert, update, and delete in one call.
TABLE
-- users
+- User
- Struct: User
- Fields:
- Id: int (primary key)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/answers/csharp.cs
index cf158fda550..f2f187d245b 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int Id;
@@ -11,7 +11,7 @@ public partial struct User
public bool Active;
}
- [Table(Name = "results")]
+ [Table(Name = "Result")]
public partial struct Result
{
[PrimaryKey] public int Id;
@@ -21,11 +21,11 @@ public partial struct Result
[Reducer]
public static void LookupUserName(ReducerContext ctx, int id)
{
- var u = ctx.Db.users.Id.Find(id);
+ var u = ctx.Db.User.Id.Find(id);
if (u.HasValue)
{
var row = u.Value;
- ctx.Db.results.Insert(new Result { Id = row.Id, Name = row.Name });
+ ctx.Db.Result.Insert(new Result { Id = row.Id, Name = row.Name });
}
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/tasks/csharp.txt
index 57ab17c7e2f..969eef30401 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_008_index_lookup/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines two tables and a reducer that looks up a row by primary-key index and writes a projection to another table.
TABLES
-- users
+- User
- Struct: User
- Fields:
- Id: int (primary key)
@@ -9,11 +9,11 @@ TABLES
- Age: int
- Active: bool
-- results
+- Result
- Struct: Result
- Fields:
- Id: int (primary key)
- Name: string
REDUCERS
-- LookupUserName: given id:int, find the users row with that Id using the index and insert (Id, Name) into results.
+- LookupUserName: given id:int, find the User row with that Id using the index and insert (Id, Name) into Result.
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/answers/csharp.cs
index 70a16112d5a..4acdbbb77d1 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int Id;
@@ -14,7 +14,7 @@ public partial struct User
[Reducer(ReducerKind.Init)]
public static void Init(ReducerContext ctx)
{
- ctx.Db.users.Insert(new User { Id = 1, Name = "Alice", Age = 30, Active = true });
- ctx.Db.users.Insert(new User { Id = 2, Name = "Bob", Age = 22, Active = false });
+ ctx.Db.User.Insert(new User { Id = 1, Name = "Alice", Age = 30, Active = true });
+ ctx.Db.User.Insert(new User { Id = 2, Name = "Bob", Age = 22, Active = false });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/tasks/csharp.txt
index 0ea58ab2e18..0f5947eacf7 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_009_init/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and an Init reducer that seeds rows on database initialization.
TABLE
-- users
+- User
- Struct: User
- Fields:
- Id: int (primary key)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/answers/csharp.cs
index b4892ec713d..15cc86d15bf 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "events")]
+ [Table(Name = "Event")]
public partial struct Event
{
[PrimaryKey, AutoInc] public int Id;
@@ -12,12 +12,12 @@ public partial struct Event
[Reducer(ReducerKind.ClientConnected)]
public static void ClientConnected(ReducerContext ctx)
{
- ctx.Db.events.Insert(new Event { Kind = "connected" });
+ ctx.Db.Event.Insert(new Event { Kind = "connected" });
}
[Reducer(ReducerKind.ClientDisconnected)]
public static void ClientDisconnected(ReducerContext ctx)
{
- ctx.Db.events.Insert(new Event { Kind = "disconnected" });
+ ctx.Db.Event.Insert(new Event { Kind = "disconnected" });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/tasks/csharp.txt
index 67e5d6553d5..02b8af70dcc 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_010_connect/tasks/csharp.txt
@@ -1,12 +1,12 @@
Write a SpacetimeDB backend module in C# that defines one table and two reducers for client lifecycle events.
TABLE
-- events
+- Event
- Struct: Event
- Fields:
- Id: int (primary key, auto-increment)
- Kind: string
REDUCERS
-- ClientConnected: when a client connects, insert exactly one row into events with Kind="connected"
-- ClientDisconnected: when a client disconnects, insert exactly one row into events with Kind="disconnected"
+- ClientConnected: when a client connects, insert exactly one row into Event with Kind="connected"
+- ClientDisconnected: when a client disconnects, insert exactly one row into Event with Kind="disconnected"
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/answers/csharp.cs
index 9599f9f149c..761e7374c63 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "results")]
+ [Table(Name = "Result")]
public partial struct Result
{
[PrimaryKey] public int Id;
@@ -14,6 +14,6 @@ public partial struct Result
[Reducer]
public static void ComputeSum(ReducerContext ctx, int id, int a, int b)
{
- ctx.Db.results.Insert(new Result { Id = id, Sum = Add(a, b) });
+ ctx.Db.Result.Insert(new Result { Id = id, Sum = Add(a, b) });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/tasks/csharp.txt
index 7bca92593fb..4a011de2de3 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/basics/t_011_helper_function/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines a table, a non-reducer helper function, and a reducer that uses the helper.
TABLE
-- results
+- Result
- Struct: Result
- Fields:
- Id: int (primary key)
@@ -11,5 +11,5 @@ HELPERS
- Add: given a:int and b:int, returns int
REDUCERS
-- ComputeSum: given id:int, a:int, b:int, insert exactly this row into results
+- ComputeSum: given id:int, a:int, b:int, insert exactly this row into Result
- (Id=id, Sum=Add(a, b))
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/answers/csharp.cs
index 6de1cc7c607..3f0c40ff5e6 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/answers/csharp.cs
@@ -9,7 +9,7 @@ public partial struct Score
public int Right;
}
- [Table(Name = "results")]
+ [Table(Name = "Result")]
public partial struct Result
{
[PrimaryKey] public int Id;
@@ -19,6 +19,6 @@ public partial struct Result
[Reducer]
public static void SetScore(ReducerContext ctx, int id, int left, int right)
{
- ctx.Db.results.Insert(new Result { Id = id, Value = new Score { Left = left, Right = right } });
+ ctx.Db.Result.Insert(new Result { Id = id, Value = new Score { Left = left, Right = right } });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/tasks/csharp.txt
index 0f4967d9aa1..380722564a6 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_012_spacetime_product_type/tasks/csharp.txt
@@ -7,12 +7,12 @@ TYPES
- Right: int
TABLE
-- results
+- Result
- Struct: Result
- Fields:
- Id: int (primary key)
- Value: Score
REDUCERS
-- SetScore: given id:int, left:int, right:int, insert exactly this row into results
+- SetScore: given id:int, left:int, right:int, insert exactly this row into Result
- (Id=id, Value=Score{Left=left, Right=right})
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/answers/csharp.cs
index 71a42097921..df439818763 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/answers/csharp.cs
@@ -11,7 +11,7 @@ public partial struct Rectangle { public int Width; public int Height; }
[Type]
public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> {}
- [Table(Name = "results")]
+ [Table(Name = "Result")]
public partial struct Result
{
[PrimaryKey] public int Id;
@@ -21,6 +21,6 @@ public partial struct Result
[Reducer]
public static void SetCircle(ReducerContext ctx, int id, int radius)
{
- ctx.Db.results.Insert(new Result { Id = id, Value = new Shape.Circle(new Circle { Radius = radius }) });
+ ctx.Db.Result.Insert(new Result { Id = id, Value = new Shape.Circle(new Circle { Radius = radius }) });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/tasks/csharp.txt
index 9b9062e8d42..e777dfb5c76 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_013_spacetime_sum_type/tasks/csharp.txt
@@ -11,12 +11,12 @@ TYPES
- Sum: Shape = Circle | Rectangle
TABLE
-- results
+- Result
- Struct: Result
- Fields:
- Id: int (primary key)
- Value: Shape
REDUCERS
-- SetCircle: given id:int and radius:int, insert exactly one row into results
+- SetCircle: given id:int and radius:int, insert exactly one row into Result
- (Id=id, Value=Circle{Radius=radius})
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/answers/csharp.cs
index 3d0735b467a..a8703779b47 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "primitives")]
+ [Table(Name = "Primitive")]
public partial struct Primitive
{
[PrimaryKey] public int Id;
@@ -17,7 +17,7 @@ public partial struct Primitive
[Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.primitives.Insert(new Primitive {
+ ctx.Db.Primitive.Insert(new Primitive {
Id = 1,
Count = 2,
Total = 3000000000,
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/tasks/csharp.txt
index 5ae2dbf0120..f1525d089a7 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_014_elementary_columns/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and seeds one row.
TABLE
-- primitives
+- Primitive
- Struct: Primitive
- Fields:
- Id: int (primary key)
@@ -13,5 +13,5 @@ TABLE
- Name: string
REDUCERS
-- Seed: insert exactly this row into primitives
+- Seed: insert exactly this row into Primitive
- (Id=1, Count=2, Total=3000000000, Price=1.5, Ratio=2.25, Active=true, Name="Alice")
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/answers/csharp.cs
index 5c5397a3abd..7b8ef06f1d2 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/answers/csharp.cs
@@ -16,7 +16,7 @@ public partial struct Position
public int Y;
}
- [Table(Name = "profiles")]
+ [Table(Name = "Profile")]
public partial struct Profile
{
[PrimaryKey] public int Id;
@@ -28,7 +28,7 @@ public partial struct Profile
[Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.profiles.Insert(new Profile {
+ ctx.Db.Profile.Insert(new Profile {
Id = 1,
Home = new Address { Street = "1 Main", Zip = 11111 },
Work = new Address { Street = "2 Broad", Zip = 22222 },
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/tasks/csharp.txt
index ed51f20f492..dfa02a7f7fb 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_015_product_type_columns/tasks/csharp.txt
@@ -11,7 +11,7 @@ TYPES
- Y: int
TABLE
-- profiles
+- Profile
- Struct: Profile
- Fields:
- Id: int (primary key)
@@ -20,5 +20,5 @@ TABLE
- Pos: Position
REDUCERS
-- Seed: insert exactly one row into profiles
+- Seed: insert exactly one row into Profile
- (Id=1, Home=Address{Street="1 Main", Zip=11111}, Work=Address{Street="2 Broad", Zip=22222}, Pos=Position{X=7, Y=9})
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/answers/csharp.cs
index d36f0f3febf..5031d954d31 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/answers/csharp.cs
@@ -11,7 +11,7 @@ public partial struct Rectangle { public int Width; public int Height; }
[Type]
public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> {}
- [Table(Name = "drawings")]
+ [Table(Name = "Drawing")]
public partial struct Drawing
{
[PrimaryKey] public int Id;
@@ -22,7 +22,7 @@ public partial struct Drawing
[Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.drawings.Insert(new Drawing {
+ ctx.Db.Drawing.Insert(new Drawing {
Id = 1,
A = new Shape.Circle(new Circle { Radius = 10 }),
B = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 })
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/tasks/csharp.txt
index 4da8f08cf23..adf87e541ca 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_016_sum_type_columns/tasks/csharp.txt
@@ -11,7 +11,7 @@ TYPES
- Sum: Shape = Circle | Rectangle
TABLE
-- drawings
+- Drawing
- Struct: Drawing
- Fields:
- Id: int (primary key)
@@ -19,5 +19,5 @@ TABLE
- B: Shape
REDUCERS
-- Seed: insert exactly one row into drawings
+- Seed: insert exactly one row into Drawing
- (Id=1, A=Circle{Radius=10}, B=Rectangle{Width=4, Height=6})
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/answers/csharp.cs
index ac84a48c74a..ab8ea16b548 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "tick_timer", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]
+ [Table(Name = "TickTimer", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]
public partial struct TickTimer
{
[PrimaryKey, AutoInc] public ulong ScheduledId;
@@ -16,7 +16,7 @@ public static void Tick(ReducerContext ctx, TickTimer schedule) { }
public static void Init(ReducerContext ctx)
{
var interval = new TimeDuration { Microseconds = 50_000 };
- ctx.Db.tick_timer.Insert(new TickTimer
+ ctx.Db.TickTimer.Insert(new TickTimer
{
ScheduledId = 0,
ScheduledAt = new ScheduleAt.Interval(interval)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/tasks/csharp.txt
index f95660a76b0..de1b1800289 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_017_scheduled_columns/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines a scheduled table and seeds one schedule entry.
TABLE
-- tick_timer
+- TickTimer
- Struct: TickTimer
- Fields:
- ScheduledId: ulong (primary key, auto-increment)
@@ -12,4 +12,4 @@ TABLE
REDUCERS
- Tick: scheduled reducer that accepts the scheduled row
-- Init: insert exactly one row into tick_timer that schedules a repeating interval of 50_000 microseconds
+- Init: insert exactly one row into TickTimer that schedules a repeating interval of 50_000 microseconds
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/answers/csharp.cs
index 2133880d758..8dc23fe28a5 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [SpacetimeDB.Table(Name = "accounts", Public = true)]
+ [SpacetimeDB.Table(Name = "Account", Public = true)]
[SpacetimeDB.Index.BTree(Name = "by_name", Columns = [nameof(Name)])]
public partial struct Account
{
@@ -14,7 +14,7 @@ public partial struct Account
[SpacetimeDB.Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.accounts.Insert(new Account { Id = 1, Email = "a@example.com", Name = "Alice" });
- ctx.Db.accounts.Insert(new Account { Id = 2, Email = "b@example.com", Name = "Bob" });
+ ctx.Db.Account.Insert(new Account { Id = 1, Email = "a@example.com", Name = "Alice" });
+ ctx.Db.Account.Insert(new Account { Id = 2, Email = "b@example.com", Name = "Bob" });
}
-}
\ No newline at end of file
+}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/tasks/csharp.txt
index 925181fd268..06e09570886 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_018_constraints/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table and seeds two rows.
TABLE
-- accounts
+- Account
- Struct: Account
- Fields:
- Id: int (primary key)
@@ -11,6 +11,6 @@ TABLE
- by_name: btree(Name)
REDUCERS
-- Seed: insert exactly these rows into accounts
+- Seed: insert exactly these rows into Account
- (Id=1, Email="a@example.com", Name="Alice")
- (Id=2, Email="b@example.com", Name="Bob")
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/answers/csharp.cs
index 6389a1a1587..0841e97a684 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/answers/csharp.cs
@@ -2,21 +2,21 @@
public static partial class Module
{
- [Table(Name = "users")]
+ [Table(Name = "User")]
public partial struct User
{
[PrimaryKey] public int UserId;
public string Name;
}
- [Table(Name = "groups")]
+ [Table(Name = "Group")]
public partial struct Group
{
[PrimaryKey] public int GroupId;
public string Title;
}
- [Table(Name = "memberships")]
+ [Table(Name = "Membership")]
[SpacetimeDB.Index.BTree(Name = "by_user", Columns = new[] { nameof(UserId) })]
[SpacetimeDB.Index.BTree(Name = "by_group", Columns = new[] { nameof(GroupId) })]
public partial struct Membership
@@ -29,14 +29,14 @@ public partial struct Membership
[Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.users.Insert(new User { UserId = 1, Name = "Alice" });
- ctx.Db.users.Insert(new User { UserId = 2, Name = "Bob" });
+ ctx.Db.User.Insert(new User { UserId = 1, Name = "Alice" });
+ ctx.Db.User.Insert(new User { UserId = 2, Name = "Bob" });
- ctx.Db.groups.Insert(new Group { GroupId = 10, Title = "Admin" });
- ctx.Db.groups.Insert(new Group { GroupId = 20, Title = "Dev" });
+ ctx.Db.Group.Insert(new Group { GroupId = 10, Title = "Admin" });
+ ctx.Db.Group.Insert(new Group { GroupId = 20, Title = "Dev" });
- ctx.Db.memberships.Insert(new Membership { Id = 1, UserId = 1, GroupId = 10 });
- ctx.Db.memberships.Insert(new Membership { Id = 2, UserId = 1, GroupId = 20 });
- ctx.Db.memberships.Insert(new Membership { Id = 3, UserId = 2, GroupId = 20 });
+ ctx.Db.Membership.Insert(new Membership { Id = 1, UserId = 1, GroupId = 10 });
+ ctx.Db.Membership.Insert(new Membership { Id = 2, UserId = 1, GroupId = 20 });
+ ctx.Db.Membership.Insert(new Membership { Id = 3, UserId = 2, GroupId = 20 });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/tasks/csharp.txt
index c28ba708ac4..694e539808a 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_019_many_to_many/tasks/csharp.txt
@@ -1,19 +1,19 @@
Write a SpacetimeDB backend module in C# that defines three tables modeling a many-to-many relationship and seeds rows.
TABLES
-- users
+- User
- Struct: User
- Fields:
- UserId: int (primary key)
- Name: string
-- groups
+- Group
- Struct: Group
- Fields:
- GroupId: int (primary key)
- Title: string
-- memberships
+- Membership
- Struct: Membership
- Fields:
- Id: int (primary key)
@@ -25,9 +25,9 @@ TABLES
REDUCERS
- Seed: insert exactly these rows
- - users: (UserId=1, Name="Alice"), (UserId=2, Name="Bob")
- - groups: (GroupId=10, Title="Admin"), (GroupId=20, Title="Dev")
- - memberships:
+ - User: (UserId=1, Name="Alice"), (UserId=2, Name="Bob")
+ - Group: (GroupId=10, Title="Admin"), (GroupId=20, Title="Dev")
+ - Membership:
- (Id=1, UserId=1, GroupId=10)
- (Id=2, UserId=1, GroupId=20)
- (Id=3, UserId=2, GroupId=20)
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/answers/csharp.cs
index 2166fcda36c..0f5c18e1ea8 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/answers/csharp.cs
@@ -2,10 +2,10 @@
public static partial class Module
{
- [Table(Name = "entities")]
+ [Table(Name = "Entity")]
public partial struct Entity { [PrimaryKey] public int Id; }
- [Table(Name = "positions")]
+ [Table(Name = "Position")]
public partial struct Position
{
[PrimaryKey] public int EntityId;
@@ -13,7 +13,7 @@ public partial struct Position
public int Y;
}
- [Table(Name = "velocities")]
+ [Table(Name = "Velocity")]
public partial struct Velocity
{
[PrimaryKey] public int EntityId;
@@ -21,7 +21,7 @@ public partial struct Velocity
public int VY;
}
- [Table(Name = "next_positions")]
+ [Table(Name = "NextPosition")]
public partial struct NextPosition
{
[PrimaryKey] public int EntityId;
@@ -32,22 +32,22 @@ public partial struct NextPosition
[Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.entities.Insert(new Entity { Id = 1 });
- ctx.Db.entities.Insert(new Entity { Id = 2 });
+ ctx.Db.Entity.Insert(new Entity { Id = 1 });
+ ctx.Db.Entity.Insert(new Entity { Id = 2 });
- ctx.Db.positions.Insert(new Position { EntityId = 1, X = 0, Y = 0 });
- ctx.Db.positions.Insert(new Position { EntityId = 2, X = 10, Y = 0 });
+ ctx.Db.Position.Insert(new Position { EntityId = 1, X = 0, Y = 0 });
+ ctx.Db.Position.Insert(new Position { EntityId = 2, X = 10, Y = 0 });
- ctx.Db.velocities.Insert(new Velocity { EntityId = 1, VX = 1, VY = 0 });
- ctx.Db.velocities.Insert(new Velocity { EntityId = 2, VX = -2, VY = 3 });
+ ctx.Db.Velocity.Insert(new Velocity { EntityId = 1, VX = 1, VY = 0 });
+ ctx.Db.Velocity.Insert(new Velocity { EntityId = 2, VX = -2, VY = 3 });
}
[Reducer]
public static void Step(ReducerContext ctx)
{
- foreach (var p in ctx.Db.positions.Iter())
+ foreach (var p in ctx.Db.Position.Iter())
{
- var velOpt = ctx.Db.velocities.EntityId.Find(p.EntityId);
+ var velOpt = ctx.Db.Velocity.EntityId.Find(p.EntityId);
if (!velOpt.HasValue) continue;
var np = new NextPosition {
@@ -56,10 +56,10 @@ public static void Step(ReducerContext ctx)
Y = p.Y + velOpt.Value.VY
};
- if (ctx.Db.next_positions.EntityId.Find(p.EntityId).HasValue)
- ctx.Db.next_positions.EntityId.Update(np);
+ if (ctx.Db.NextPosition.EntityId.Find(p.EntityId).HasValue)
+ ctx.Db.NextPosition.EntityId.Update(np);
else
- ctx.Db.next_positions.Insert(np);
+ ctx.Db.NextPosition.Insert(np);
}
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/tasks/csharp.txt
index 6674f0928d4..e0b83d6f8da 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_020_ecs/tasks/csharp.txt
@@ -1,26 +1,26 @@
Write a SpacetimeDB backend module in C# that models a minimal ECS and computes next positions.
TABLES
-- entities
+- Entity
- Struct: Entity
- Fields:
- Id: int (primary key)
-- positions
+- Position
- Struct: Position
- Fields:
- EntityId: int (primary key)
- X: int
- Y: int
-- velocities
+- Velocity
- Struct: Velocity
- Fields:
- EntityId: int (primary key)
- VX: int
- VY: int
-- next_positions
+- NextPosition
- Struct: NextPosition
- Fields:
- EntityId: int (primary key)
@@ -31,4 +31,4 @@ REDUCERS
- Seed: insert two entities with positions and velocities:
- (Id=1, Pos=(0,0), Vel=(1,0))
- (Id=2, Pos=(10,0), Vel=(-2,3))
-- Step: for each Position, find Velocity by EntityId; compute (X+VX, Y+VY) and upsert into next_positions by EntityId
+- Step: for each Position, find Velocity by EntityId; compute (X+VX, Y+VY) and upsert into NextPosition by EntityId
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/answers/csharp.cs b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/answers/csharp.cs
index 621b2391637..acc88e552e6 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/answers/csharp.cs
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/answers/csharp.cs
@@ -2,7 +2,7 @@
public static partial class Module
{
- [Table(Name = "logs")]
+ [Table(Name = "Log")]
[SpacetimeDB.Index.BTree(Name = "by_user_day", Columns = new[] { nameof(UserId), nameof(Day) })]
public partial struct Log
{
@@ -15,8 +15,8 @@ public partial struct Log
[Reducer]
public static void Seed(ReducerContext ctx)
{
- ctx.Db.logs.Insert(new Log { Id = 1, UserId = 7, Day = 1, Message = "a" });
- ctx.Db.logs.Insert(new Log { Id = 2, UserId = 7, Day = 2, Message = "b" });
- ctx.Db.logs.Insert(new Log { Id = 3, UserId = 9, Day = 1, Message = "c" });
+ ctx.Db.Log.Insert(new Log { Id = 1, UserId = 7, Day = 1, Message = "a" });
+ ctx.Db.Log.Insert(new Log { Id = 2, UserId = 7, Day = 2, Message = "b" });
+ ctx.Db.Log.Insert(new Log { Id = 3, UserId = 9, Day = 1, Message = "c" });
}
}
diff --git a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/tasks/csharp.txt b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/tasks/csharp.txt
index a2c4510a5df..49ac302411e 100644
--- a/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/tasks/csharp.txt
+++ b/tools/xtask-llm-benchmark/src/benchmarks/schema/t_021_multi_column_index/tasks/csharp.txt
@@ -1,7 +1,7 @@
Write a SpacetimeDB backend module in C# that defines one table with a multi-column B-Tree index and seeds rows.
TABLE
-- logs
+- Log
- Struct: Log
- Fields:
- Id: int (primary key)
@@ -12,7 +12,7 @@ TABLE
- by_user_day: btree(UserId, Day)
REDUCERS
-- Seed: insert exactly these rows into logs
+- Seed: insert exactly these rows into Log
- (Id=1, UserId=7, Day=1, Message="a")
- (Id=2, UserId=7, Day=2, Message="b")
- (Id=3, UserId=9, Day=1, Message="c")
diff --git a/tools/xtask-llm-benchmark/src/bin/llm_benchmark.rs b/tools/xtask-llm-benchmark/src/bin/llm_benchmark.rs
index 53e7eae176a..2b13f168691 100644
--- a/tools/xtask-llm-benchmark/src/bin/llm_benchmark.rs
+++ b/tools/xtask-llm-benchmark/src/bin/llm_benchmark.rs
@@ -16,7 +16,8 @@ use xtask_llm_benchmark::bench::runner::{
};
use xtask_llm_benchmark::bench::types::{BenchRunContext, RouteRun, RunConfig};
use xtask_llm_benchmark::context::constants::{
- docs_benchmark_details, docs_benchmark_summary, llm_comparison_details, llm_comparison_summary, ALL_MODES,
+ docs_benchmark_comment, docs_benchmark_details, docs_benchmark_summary, llm_comparison_details,
+ llm_comparison_summary, ALL_MODES,
};
use xtask_llm_benchmark::context::{build_context, compute_context_hash, docs_dir};
use xtask_llm_benchmark::eval::Lang;
@@ -85,8 +86,14 @@ enum Commands {
/// Quickfix CI by running a minimal OpenAI model set.
CiQuickfix,
+ /// Generate markdown comment for GitHub PR (compares against master baseline).
+ CiComment(CiCommentArgs),
+
/// Regenerate summary.json from details.json (optionally custom paths).
Summary(SummaryArgs),
+
+ /// Analyze benchmark failures and generate a human-readable markdown report.
+ Analyze(AnalyzeArgs),
}
#[derive(Args, Debug, Clone)]
@@ -150,6 +157,32 @@ struct SummaryArgs {
summary: Option,
}
+#[derive(Args, Debug, Clone)]
+struct AnalyzeArgs {
+ /// Input details.json file (default: docs-benchmark-details.json)
+ #[arg(long)]
+ details: Option,
+
+ /// Output markdown file (default: docs-benchmark-analysis.md)
+ #[arg(long, short)]
+ output: Option,
+
+ /// Only analyze failures for a specific language (rust, csharp)
+ #[arg(long)]
+ lang: Option,
+}
+
+#[derive(Args, Debug, Clone)]
+struct CiCommentArgs {
+ /// Output markdown file (default: docs-benchmark-comment.md)
+ #[arg(long, short)]
+ output: Option,
+
+ /// Git ref to compare against for baseline (default: origin/master)
+ #[arg(long, default_value = "origin/master")]
+ baseline_ref: String,
+}
+
/// Local wrapper so we can parse Vendor without orphan-rule issues.
#[derive(Clone, Debug)]
struct VendorArg(pub Vendor);
@@ -171,7 +204,9 @@ fn main() -> Result<()> {
Commands::Run(args) => cmd_run(args),
Commands::CiCheck(args) => cmd_ci_check(args),
Commands::CiQuickfix => cmd_ci_quickfix(),
+ Commands::CiComment(args) => cmd_ci_comment(args),
Commands::Summary(args) => cmd_summary(args),
+ Commands::Analyze(args) => cmd_analyze(args),
}
}
@@ -408,6 +443,199 @@ fn cmd_ci_quickfix() -> Result<()> {
Ok(())
}
+/* --------------------------- ci-comment --------------------------- */
+
+fn cmd_ci_comment(args: CiCommentArgs) -> Result<()> {
+ use std::process::Command;
+
+ let summary_path = docs_benchmark_summary();
+ let output_path = args.output.unwrap_or_else(docs_benchmark_comment);
+
+ // Load current summary
+ let summary: Summary =
+ load_summary(&summary_path).with_context(|| format!("load summary file at {:?}", summary_path))?;
+
+ // Try to load baseline from git ref
+ let baseline: Option = {
+ let relative_path = "docs/llms/docs-benchmark-summary.json";
+ let output = Command::new("git")
+ .args(["show", &format!("{}:{}", args.baseline_ref, relative_path)])
+ .output();
+
+ match output {
+ Ok(out) if out.status.success() => {
+ let json = String::from_utf8_lossy(&out.stdout);
+ match serde_json::from_str(&json) {
+ Ok(s) => {
+ println!("Loaded baseline from {}", args.baseline_ref);
+ Some(s)
+ }
+ Err(e) => {
+ println!("Warning: Could not parse baseline JSON: {}", e);
+ None
+ }
+ }
+ }
+ Ok(out) => {
+ let stderr = String::from_utf8_lossy(&out.stderr);
+ println!(
+ "Note: Could not load baseline from {} (file may not exist yet): {}",
+ args.baseline_ref,
+ stderr.trim()
+ );
+ None
+ }
+ Err(e) => {
+ println!("Warning: git command failed: {}", e);
+ None
+ }
+ }
+ };
+
+ // Generate markdown
+ let markdown = generate_comment_markdown(&summary, baseline.as_ref());
+
+ // Write to file
+ if let Some(parent) = output_path.parent() {
+ fs::create_dir_all(parent)?;
+ }
+ fs::write(&output_path, &markdown)?;
+ println!("Comment markdown written to: {}", output_path.display());
+
+ Ok(())
+}
+
+/// Generate the markdown comment for GitHub PR.
+fn generate_comment_markdown(summary: &Summary, baseline: Option<&Summary>) -> String {
+ let rust_results = summary
+ .by_language
+ .get("rust")
+ .and_then(|l| l.modes.get("rustdoc_json"))
+ .and_then(|m| m.models.get("GPT-5"));
+ let csharp_results = summary
+ .by_language
+ .get("csharp")
+ .and_then(|l| l.modes.get("docs"))
+ .and_then(|m| m.models.get("GPT-5"));
+
+ let rust_baseline = baseline
+ .and_then(|b| b.by_language.get("rust"))
+ .and_then(|l| l.modes.get("rustdoc_json"))
+ .and_then(|m| m.models.get("GPT-5"));
+ let csharp_baseline = baseline
+ .and_then(|b| b.by_language.get("csharp"))
+ .and_then(|l| l.modes.get("docs"))
+ .and_then(|m| m.models.get("GPT-5"));
+
+ fn format_pct(val: f32) -> String {
+ format!("{:.1}%", val)
+ }
+
+ fn format_diff(current: f32, baseline: Option) -> String {
+ match baseline {
+ Some(b) => {
+ let diff = current - b;
+ if diff.abs() < 0.1 {
+ String::new()
+ } else {
+ let sign = if diff > 0.0 { "+" } else { "" };
+ let arrow = if diff > 0.0 { "⬆️" } else { "⬇️" };
+ format!(" {} {}{:.1}%", arrow, sign, diff)
+ }
+ }
+ None => String::new(),
+ }
+ }
+
+ let mut md = String::new();
+ md.push_str("## LLM Benchmark Results (ci-quickfix)\n\n");
+ md.push_str("| Language | Mode | Category | Tests Passed | Task Pass % |\n");
+ md.push_str("|----------|------|----------|--------------|-------------|\n");
+
+ if let Some(results) = rust_results {
+ let base_cats = rust_baseline.map(|b| &b.categories);
+
+ if let Some(c) = results.categories.get("basics") {
+ let b = base_cats.and_then(|cats| cats.get("basics"));
+ let diff = format_diff(c.task_pass_pct, b.map(|x| x.task_pass_pct));
+ md.push_str(&format!(
+ "| Rust | rustdoc_json | basics | {}/{} | {}{} |\n",
+ c.passed_tests,
+ c.total_tests,
+ format_pct(c.task_pass_pct),
+ diff
+ ));
+ }
+ if let Some(c) = results.categories.get("schema") {
+ let b = base_cats.and_then(|cats| cats.get("schema"));
+ let diff = format_diff(c.task_pass_pct, b.map(|x| x.task_pass_pct));
+ md.push_str(&format!(
+ "| Rust | rustdoc_json | schema | {}/{} | {}{} |\n",
+ c.passed_tests,
+ c.total_tests,
+ format_pct(c.task_pass_pct),
+ diff
+ ));
+ }
+ let diff = format_diff(
+ results.totals.task_pass_pct,
+ rust_baseline.map(|b| b.totals.task_pass_pct),
+ );
+ md.push_str(&format!(
+ "| Rust | rustdoc_json | **total** | {}/{} | **{}**{} |\n",
+ results.totals.passed_tests,
+ results.totals.total_tests,
+ format_pct(results.totals.task_pass_pct),
+ diff
+ ));
+ }
+
+ if let Some(results) = csharp_results {
+ let base_cats = csharp_baseline.map(|b| &b.categories);
+
+ if let Some(c) = results.categories.get("basics") {
+ let b = base_cats.and_then(|cats| cats.get("basics"));
+ let diff = format_diff(c.task_pass_pct, b.map(|x| x.task_pass_pct));
+ md.push_str(&format!(
+ "| C# | docs | basics | {}/{} | {}{} |\n",
+ c.passed_tests,
+ c.total_tests,
+ format_pct(c.task_pass_pct),
+ diff
+ ));
+ }
+ if let Some(c) = results.categories.get("schema") {
+ let b = base_cats.and_then(|cats| cats.get("schema"));
+ let diff = format_diff(c.task_pass_pct, b.map(|x| x.task_pass_pct));
+ md.push_str(&format!(
+ "| C# | docs | schema | {}/{} | {}{} |\n",
+ c.passed_tests,
+ c.total_tests,
+ format_pct(c.task_pass_pct),
+ diff
+ ));
+ }
+ let diff = format_diff(
+ results.totals.task_pass_pct,
+ csharp_baseline.map(|b| b.totals.task_pass_pct),
+ );
+ md.push_str(&format!(
+ "| C# | docs | **total** | {}/{} | **{}**{} |\n",
+ results.totals.passed_tests,
+ results.totals.total_tests,
+ format_pct(results.totals.task_pass_pct),
+ diff
+ ));
+ }
+
+ if baseline.is_some() {
+ md.push_str("\n_Compared against master branch baseline_\n");
+ }
+ md.push_str(&format!("\nGenerated at: {}\n", summary.generated_at));
+
+ md
+}
+
/* --------------------------- helpers --------------------------- */
fn short_hash(s: &str) -> &str {
@@ -424,7 +652,7 @@ fn run_mode_benchmarks(
llm_provider: Option<&Arc>,
) -> Result<()> {
let lang_str = lang.as_str();
- let context = build_context(mode)?;
+ let context = build_context(mode, Some(lang))?;
let hash = compute_context_hash(mode).with_context(|| format!("compute docs hash for `{mode}`/{}", lang_str))?;
println!("{:<12} [{:<10}] hash: {}", mode, lang_str, short_hash(&hash));
@@ -682,3 +910,304 @@ fn cmd_summary(args: SummaryArgs) -> Result<()> {
println!("Summary written to: {}", out_path.display());
Ok(())
}
+
+fn cmd_analyze(args: AnalyzeArgs) -> Result<()> {
+ use xtask_llm_benchmark::results::schema::Results;
+
+ let details_path = args.details.unwrap_or_else(docs_benchmark_details);
+ let output_path = args.output.unwrap_or_else(|| {
+ details_path
+ .parent()
+ .unwrap_or(Path::new("."))
+ .join("docs-benchmark-analysis.md")
+ });
+
+ println!("Analyzing benchmark results from: {}", details_path.display());
+
+ // Load the details file
+ let content =
+ fs::read_to_string(&details_path).with_context(|| format!("Failed to read {}", details_path.display()))?;
+ let results: Results = serde_json::from_str(&content).with_context(|| "Failed to parse details.json")?;
+
+ // Collect failures
+ let mut failures: Vec = Vec::new();
+
+ for lang_entry in &results.languages {
+ // Skip if filtering by language
+ if let Some(filter_lang) = &args.lang {
+ if lang_entry.lang != filter_lang.as_str() {
+ continue;
+ }
+ }
+
+ let golden_answers = &lang_entry.golden_answers;
+
+ for mode_entry in &lang_entry.modes {
+ for model_entry in &mode_entry.models {
+ for (task_id, outcome) in &model_entry.tasks {
+ if outcome.passed_tests < outcome.total_tests {
+ // This task has failures
+ let golden = golden_answers
+ .get(task_id)
+ .or_else(|| {
+ // Try with category prefix stripped
+ task_id.split('/').next_back().and_then(|t| golden_answers.get(t))
+ })
+ .map(|g| g.answer.clone());
+
+ failures.push(FailureInfo {
+ lang: lang_entry.lang.clone(),
+ mode: mode_entry.mode.clone(),
+ model: model_entry.name.clone(),
+ task: task_id.clone(),
+ passed: outcome.passed_tests,
+ total: outcome.total_tests,
+ llm_output: outcome.llm_output.clone(),
+ golden_answer: golden,
+ scorer_details: outcome.scorer_details.clone(),
+ });
+ }
+ }
+ }
+ }
+ }
+
+ if failures.is_empty() {
+ println!("No failures found!");
+ fs::write(
+ &output_path,
+ "# Benchmark Analysis\n\nNo failures found. All tests passed!",
+ )?;
+ println!("Analysis written to: {}", output_path.display());
+ return Ok(());
+ }
+
+ println!("Found {} failing test(s). Generating analysis...", failures.len());
+
+ // Build prompt for LLM
+ let prompt = build_analysis_prompt(&failures);
+
+ // Initialize runtime and LLM provider
+ let runtime = tokio::runtime::Builder::new_current_thread().enable_all().build()?;
+ let provider = make_provider_from_env()?;
+
+ // Use a fast model for analysis
+ let route = ModelRoute {
+ display_name: "gpt-4o-mini",
+ api_model: "gpt-4o-mini",
+ vendor: Vendor::OpenAi,
+ };
+
+ use xtask_llm_benchmark::llm::prompt::BuiltPrompt;
+
+ let built_prompt = BuiltPrompt {
+ system: Some(
+ "You are an expert at analyzing SpacetimeDB benchmark failures. \
+ Analyze the test failures and provide actionable insights in markdown format."
+ .to_string(),
+ ),
+ static_prefix: None,
+ segments: vec![xtask_llm_benchmark::llm::segmentation::Segment::new("user", prompt)],
+ };
+
+ let analysis = runtime.block_on(provider.generate(&route, &built_prompt))?;
+
+ // Write markdown output
+ let markdown = format!(
+ "# Benchmark Failure Analysis\n\n\
+ Generated from: `{}`\n\n\
+ ## Summary\n\n\
+ - **Total failures analyzed**: {}\n\n\
+ ## Analysis\n\n\
+ {}\n",
+ details_path.display(),
+ failures.len(),
+ analysis
+ );
+
+ fs::write(&output_path, markdown)?;
+ println!("Analysis written to: {}", output_path.display());
+
+ Ok(())
+}
+
+#[allow(dead_code)]
+struct FailureInfo {
+ lang: String,
+ mode: String,
+ model: String,
+ task: String,
+ passed: u32,
+ total: u32,
+ llm_output: Option,
+ golden_answer: Option,
+ scorer_details: Option>,
+}
+
+/// Extract concise failure reasons from scorer_details using typed extraction.
+fn extract_failure_reasons(details: &HashMap) -> Vec {
+ details
+ .iter()
+ .filter_map(|(scorer_name, score)| {
+ score
+ .failure_reason()
+ .map(|reason| format!("{}: {}", scorer_name, reason))
+ })
+ .collect()
+}
+
+/// Categorize a failure by its type based on scorer details.
+fn categorize_failure(f: &FailureInfo) -> &'static str {
+ let reasons = f
+ .scorer_details
+ .as_ref()
+ .map(extract_failure_reasons)
+ .unwrap_or_default();
+
+ let reasons_str = reasons.join(" ");
+ if reasons_str.contains("tables differ") {
+ "table_naming"
+ } else if reasons_str.contains("timed out") {
+ "timeout"
+ } else if reasons_str.contains("publish failed") || reasons_str.contains("compile") {
+ "compile"
+ } else {
+ "other"
+ }
+}
+
+/// Build the analysis section for failures of a specific language.
+fn build_language_section(lang: &str, failures: &[&FailureInfo], prompt: &mut String) {
+ let lang_display = match lang {
+ "rust" => "Rust",
+ "csharp" => "C#",
+ _ => lang,
+ };
+
+ prompt.push_str(&format!("# {} Failures ({} total)\n\n", lang_display, failures.len()));
+
+ // Group by failure type
+ let table_naming: Vec<_> = failures
+ .iter()
+ .filter(|f| categorize_failure(f) == "table_naming")
+ .collect();
+ let compile: Vec<_> = failures.iter().filter(|f| categorize_failure(f) == "compile").collect();
+ let timeout: Vec<_> = failures.iter().filter(|f| categorize_failure(f) == "timeout").collect();
+ let other: Vec<_> = failures.iter().filter(|f| categorize_failure(f) == "other").collect();
+
+ // Table naming issues
+ if !table_naming.is_empty() {
+ prompt.push_str(&format!("## Table Naming Issues ({} failures)\n\n", table_naming.len()));
+ prompt.push_str("The LLM is using incorrect table names:\n\n");
+
+ for f in table_naming.iter().take(5) {
+ let reasons = f
+ .scorer_details
+ .as_ref()
+ .map(extract_failure_reasons)
+ .unwrap_or_default();
+ prompt.push_str(&format!("- **{}**: {}\n", f.task, reasons.join(", ")));
+ }
+ if table_naming.len() > 5 {
+ prompt.push_str(&format!("- ...and {} more similar failures\n", table_naming.len() - 5));
+ }
+ prompt.push('\n');
+ }
+
+ // Compile/publish errors
+ if !compile.is_empty() {
+ prompt.push_str(&format!("## Compile/Publish Errors ({} failures)\n\n", compile.len()));
+
+ for f in compile.iter().take(3) {
+ let reasons = f
+ .scorer_details
+ .as_ref()
+ .map(extract_failure_reasons)
+ .unwrap_or_default();
+ prompt.push_str(&format!("### {}\n", f.task));
+ prompt.push_str(&format!("**Error**: {}\n\n", reasons.join(", ")));
+
+ if let Some(llm_out) = &f.llm_output {
+ let truncated = if llm_out.len() > 600 {
+ format!("{}...", &llm_out[..600])
+ } else {
+ llm_out.clone()
+ };
+ prompt.push_str(&format!("**LLM Output**:\n```\n{}\n```\n\n", truncated));
+ }
+ }
+ }
+
+ // Timeout issues
+ if !timeout.is_empty() {
+ prompt.push_str(&format!("## Timeout Issues ({} failures)\n\n", timeout.len()));
+ for f in &timeout {
+ prompt.push_str(&format!("- {}\n", f.task));
+ }
+ prompt.push('\n');
+ }
+
+ // Other failures
+ if !other.is_empty() {
+ prompt.push_str(&format!("## Other Failures ({} failures)\n\n", other.len()));
+
+ for f in &other {
+ let reasons = f
+ .scorer_details
+ .as_ref()
+ .map(extract_failure_reasons)
+ .unwrap_or_default();
+ prompt.push_str(&format!("### {} - {}/{} passed\n", f.task, f.passed, f.total));
+ prompt.push_str(&format!("**Issues**: {}\n\n", reasons.join(", ")));
+
+ if let Some(llm_out) = &f.llm_output {
+ let truncated = if llm_out.len() > 600 {
+ format!("{}...", &llm_out[..600])
+ } else {
+ llm_out.clone()
+ };
+ prompt.push_str(&format!("**LLM Output**:\n```\n{}\n```\n\n", truncated));
+ }
+
+ if let Some(golden) = &f.golden_answer {
+ let truncated = if golden.len() > 600 {
+ format!("{}...", &golden[..600])
+ } else {
+ golden.clone()
+ };
+ prompt.push_str(&format!("**Expected**:\n```\n{}\n```\n\n", truncated));
+ }
+ }
+ }
+}
+
+fn build_analysis_prompt(failures: &[FailureInfo]) -> String {
+ let mut prompt = String::from(
+ "Analyze the following SpacetimeDB benchmark test failures, organized by language. \
+ For each language, identify the root causes and suggest specific documentation fixes.\n\n\
+ Focus on SPECIFIC, ACTIONABLE documentation changes.\n\n",
+ );
+
+ // Group failures by language
+ let rust_failures: Vec<_> = failures.iter().filter(|f| f.lang == "rust").collect();
+ let csharp_failures: Vec<_> = failures.iter().filter(|f| f.lang == "csharp").collect();
+
+ // Build sections for each language
+ if !rust_failures.is_empty() {
+ build_language_section("rust", &rust_failures, &mut prompt);
+ }
+
+ if !csharp_failures.is_empty() {
+ build_language_section("csharp", &csharp_failures, &mut prompt);
+ }
+
+ prompt.push_str(
+ "\n---\n\n## Provide your analysis:\n\n\
+ For EACH language (Rust and C#), provide:\n\
+ 1. **Root Causes**: What specific documentation issues caused these failures?\n\
+ 2. **Recommendations**: List specific documentation files/sections to update and exact changes to make\n\
+ 3. **Priority**: Which fixes would have the highest impact for that language?\n",
+ );
+
+ prompt
+}
diff --git a/tools/xtask-llm-benchmark/src/context/combine.rs b/tools/xtask-llm-benchmark/src/context/combine.rs
index 9fb3f0208ab..cc4fc65932d 100644
--- a/tools/xtask-llm-benchmark/src/context/combine.rs
+++ b/tools/xtask-llm-benchmark/src/context/combine.rs
@@ -1,10 +1,14 @@
use crate::context::paths::resolve_mode_paths;
+use crate::eval::lang::Lang;
use anyhow::{anyhow, Context, Result};
+use regex::Regex;
use serde_json::Value;
use std::fs;
use std::path::Path;
+use std::sync::LazyLock;
-pub fn build_context(mode: &str) -> Result {
+/// Build context for the given mode, optionally filtering tabs for a specific language.
+pub fn build_context(mode: &str, lang: Option) -> Result {
if mode == "rustdoc_json" {
return build_context_from_rustdoc_json();
}
@@ -14,6 +18,14 @@ pub fn build_context(mode: &str) -> Result {
for p in files {
let rel = rel_display(&p);
let contents = fs::read_to_string(&p).with_context(|| format!("read {}", rel))?;
+
+ // Filter tabs if a language is specified
+ let contents = if let Some(lang) = lang {
+ filter_tabs_for_lang(&contents, lang)
+ } else {
+ contents
+ };
+
out.push_str("\n\n---\n");
out.push_str(&format!("// file: {}\n\n", rel));
out.push_str(&contents);
@@ -21,6 +33,47 @@ pub fn build_context(mode: &str) -> Result {
Ok(out)
}
+/// Filter `` blocks to only include the tab matching the target language.
+/// This removes noise from other languages and helps the LLM focus on relevant examples.
+/// Filters both `server-language` and `client-language` tabs.
+fn filter_tabs_for_lang(content: &str, lang: Lang) -> String {
+ // Map Lang to the tab value used in docs for server and client tabs
+ let tab_value = match lang {
+ Lang::CSharp => "csharp",
+ Lang::Rust => "rust",
+ };
+
+ // Regex to match ... blocks
+ static LANG_TABS_RE: LazyLock = LazyLock::new(|| {
+ Regex::new(r#"(?s)]*>(.+?)"#).unwrap()
+ });
+
+ // Regex to extract individual blocks
+ static TAB_ITEM_RE: LazyLock =
+ LazyLock::new(|| Regex::new(r#"(?s)]*>(.+?)"#).unwrap());
+
+ LANG_TABS_RE
+ .replace_all(content, |caps: ®ex::Captures| {
+ let tabs_inner = &caps[1];
+
+ // Find the TabItem matching our target language
+ for tab_cap in TAB_ITEM_RE.captures_iter(tabs_inner) {
+ let value = &tab_cap[1];
+ let tab_content = &tab_cap[2];
+
+ if value == tab_value {
+ // Return just the content, stripping the TabItem wrapper
+ return tab_content.trim().to_string();
+ }
+ }
+
+ // If no matching tab found, remove the entire tabs block
+ // (e.g., client-language tabs with only cpp/blueprint for Unreal)
+ String::new()
+ })
+ .into_owned()
+}
+
fn build_context_from_rustdoc_json() -> Result {
let files = resolve_mode_paths("rustdoc_json")?;
let json_path = files
diff --git a/tools/xtask-llm-benchmark/src/context/constants.rs b/tools/xtask-llm-benchmark/src/context/constants.rs
index df749a65e07..9bd21bd943b 100644
--- a/tools/xtask-llm-benchmark/src/context/constants.rs
+++ b/tools/xtask-llm-benchmark/src/context/constants.rs
@@ -6,6 +6,7 @@ pub const RUSTDOC_CRATE_ROOT_DEFAULT: &str = "../../crates/bindings";
// Docs benchmark files (CI - single "best" model to test documentation quality)
pub const DOCS_BENCHMARK_DETAILS_DEFAULT: &str = "../../docs/llms/docs-benchmark-details.json";
pub const DOCS_BENCHMARK_SUMMARY_DEFAULT: &str = "../../docs/llms/docs-benchmark-summary.json";
+pub const DOCS_BENCHMARK_COMMENT_DEFAULT: &str = "../../docs/llms/docs-benchmark-comment.md";
// LLM comparison files (manual runs - all models to compare LLM performance)
pub const LLM_COMPARISON_DETAILS_DEFAULT: &str = "../../docs/llms/llm-comparison-details.json";
@@ -27,6 +28,10 @@ pub fn docs_benchmark_details() -> PathBuf {
pub fn docs_benchmark_summary() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(DOCS_BENCHMARK_SUMMARY_DEFAULT)
}
+#[inline]
+pub fn docs_benchmark_comment() -> PathBuf {
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(DOCS_BENCHMARK_COMMENT_DEFAULT)
+}
// LLM comparison paths (manual)
#[inline]
diff --git a/tools/xtask-llm-benchmark/src/eval/types.rs b/tools/xtask-llm-benchmark/src/eval/types.rs
index d6c63d9721c..60013e95281 100644
--- a/tools/xtask-llm-benchmark/src/eval/types.rs
+++ b/tools/xtask-llm-benchmark/src/eval/types.rs
@@ -15,6 +15,75 @@ pub struct ScoreDetails {
pub notes: Value,
}
+impl ScoreDetails {
+ /// Extract a human-readable failure reason from the notes.
+ pub fn failure_reason(&self) -> Option {
+ if self.pass {
+ return None;
+ }
+
+ let notes = self.notes.as_object()?;
+
+ // Check for error message (publish/compile errors, timeouts)
+ if let Some(err) = notes.get("error").and_then(|v| v.as_str()) {
+ let short = if err.len() > 150 { &err[..150] } else { err };
+ return Some(short.to_string());
+ }
+
+ // Check for table diff (schema_parity scorer)
+ if let Some(tables_diff) = notes.get("tables_diff") {
+ if !tables_diff.is_null() {
+ if let Ok(diff) = serde_json::from_value::(tables_diff.clone()) {
+ if !diff.only_golden.is_empty() || !diff.only_llm.is_empty() {
+ let golden_names: Vec<_> = diff.only_golden.keys().collect();
+ let llm_names: Vec<_> = diff.only_llm.keys().collect();
+ return Some(format!(
+ "tables differ - expected {:?}, got {:?}",
+ golden_names, llm_names
+ ));
+ }
+ }
+ }
+ }
+
+ // Check for reducer diff
+ if let Some(reducers_diff) = notes.get("reducers_diff") {
+ if !reducers_diff.is_null() {
+ if let Ok(diff) = serde_json::from_value::(reducers_diff.clone()) {
+ if !diff.only_golden.is_empty() || !diff.only_llm.is_empty() {
+ return Some(format!(
+ "reducers differ - expected {:?}, got {:?}",
+ diff.only_golden, diff.only_llm
+ ));
+ }
+ }
+ }
+ }
+
+ Some("failed".to_string())
+ }
+}
+
+/// Diff structure for table comparisons in schema_parity scorer.
+#[derive(Debug, Deserialize, Default)]
+pub struct SchemaDiff {
+ #[serde(default)]
+ pub only_golden: std::collections::BTreeMap,
+ #[serde(default)]
+ pub only_llm: std::collections::BTreeMap,
+ #[serde(default)]
+ pub changed: std::collections::BTreeMap,
+}
+
+/// Diff structure for reducer comparisons.
+#[derive(Debug, Deserialize, Default)]
+pub struct ReducerDiff {
+ #[serde(default)]
+ pub only_golden: Vec,
+ #[serde(default)]
+ pub only_llm: Vec,
+}
+
pub struct ReducerDataParityConfig<'a> {
pub src_file: &'a str,
pub route_tag: &'a str,
diff --git a/tools/xtask-llm-benchmark/src/llm/clients/anthropic.rs b/tools/xtask-llm-benchmark/src/llm/clients/anthropic.rs
index aa8c2287904..e185903d7a2 100644
--- a/tools/xtask-llm-benchmark/src/llm/clients/anthropic.rs
+++ b/tools/xtask-llm-benchmark/src/llm/clients/anthropic.rs
@@ -41,14 +41,20 @@ impl AnthropicClient {
let allowance = ctx_limit.saturating_sub(reserve);
static_prefix = deterministic_trim_prefix(&static_prefix, allowance);
- // Build messages, putting the context first if you want cache wins
+ // Build messages, putting the context first for cache wins
let (system_json, mut messages) = build_anthropic_messages(system.as_deref(), &segs);
if !static_prefix.is_empty() {
+ // Mark static prefix for caching - this content will be cached and reused
+ // across multiple requests, significantly reducing costs for repeated prefixes
messages.insert(
0,
serde_json::json!({
"role":"user",
- "content":[{"type":"text","text": static_prefix}]
+ "content":[{
+ "type":"text",
+ "text": static_prefix,
+ "cache_control": {"type": "ephemeral"}
+ }]
}),
);
}
@@ -81,6 +87,11 @@ impl AnthropicClient {
HeaderName::from_static("anthropic-version"),
HeaderValue::from_static("2023-06-01"),
);
+ // Enable prompt caching - reduces cost by ~90% for repeated prefixes
+ hm.insert(
+ HeaderName::from_static("anthropic-beta"),
+ HeaderValue::from_static("prompt-caching-2024-07-31"),
+ );
let url = self.url_messages();
let (status, body) = self.post_with_retries(&url, hm, &req).await?;
diff --git a/tools/xtask-llm-benchmark/src/llm/clients/openai.rs b/tools/xtask-llm-benchmark/src/llm/clients/openai.rs
index ef6eeda530c..2c58c784ee4 100644
--- a/tools/xtask-llm-benchmark/src/llm/clients/openai.rs
+++ b/tools/xtask-llm-benchmark/src/llm/clients/openai.rs
@@ -48,6 +48,9 @@ impl OpenAiClient {
};
// Build input (system + trimmed prefix + untouched segments)
+ // Note: OpenAI's Responses API automatically caches repeated prefixes for
+ // gpt-4o, gpt-4.1, gpt-5, and similar models. No explicit cache_control needed.
+ // The static_prefix (docs) is placed first to maximize cache hits across tasks.
let input = build_openai_responses_input(system.as_deref(), static_opt, &segs);
if debug_llm_verbose() {
diff --git a/tools/xtask-llm-benchmark/src/llm/mod.rs b/tools/xtask-llm-benchmark/src/llm/mod.rs
index e47b9783422..5d3e96670a5 100644
--- a/tools/xtask-llm-benchmark/src/llm/mod.rs
+++ b/tools/xtask-llm-benchmark/src/llm/mod.rs
@@ -3,7 +3,7 @@ pub mod config;
pub mod model_routes;
pub mod prompt;
pub mod provider;
-mod segmentation;
+pub mod segmentation;
pub mod types;
pub use config::make_provider_from_env;