Skip to content

Conversation

@cloutiertyler
Copy link
Contributor

Description of Changes

Two documentation improvements:

  1. Reducers documentation: Clarified that using global/static variables in reducers is undefined behavior, not just "values won't persist". Added six specific reasons why this is undefined:

    • Fresh execution environments
    • Module updates
    • Concurrent execution
    • Crash recovery
    • Non-transactional updates
    • Replay safety
  2. Access permissions documentation: Replaced the "Combining Both Techniques" example that used indexes on Option fields (which SpacetimeDB doesn't support) with a working example that filters by a required department field instead.

API and ABI breaking changes

None. Documentation only.

Expected complexity level and risk

1 - Documentation changes only.

Testing

  • Verify the reducers warning is clear and accurate
  • Verify the access permissions example compiles and makes sense

@cloutiertyler cloutiertyler changed the title Tyler/claude docs 6 Small docs improvement Jan 20, 2026
cloutiertyler and others added 21 commits January 20, 2026 21:38
- Update table_name() to convert lowercase singular names to appropriate
  case per language (C#: PascalCase, Rust: snake_case)
- Update all spec.rs files to use table_name() instead of hardcoded names
- Update Rust task prompts to use singular table names (users → user)
- Update Rust golden answers to use singular table/struct names and
  accessor methods (ctx.db.users() → ctx.db.user())

This fixes the C# benchmark test failures caused by table name mismatches
where the LLM generates "User" but tests query for "users".
- Add compute_processed_context_hash() for language-specific hash computation
  after tab filtering is applied
- Update CI check to verify both rustdoc_json and docs modes for Rust
- Fix hash-only mode to skip golden builds
- Update benchmark analysis with latest results
The llm-benchmark-update workflow builds the tool from master to save
benchmark results. The CI check must also use master's tool to compute
hashes the same way, otherwise hash mismatches occur when the PR branch
has different hash computation logic.
- Add detection for WASI SDK tar extraction failures (wasi-sdk + tar,
  MSB3073 with exit code 2) as transient errors that should be retried
- Increase max retries from 2 to 3 for build commands
- Add 1-3 seconds of jitter to retry delays to desynchronize parallel
  builds that fail simultaneously, reducing the chance of repeated
  collisions
…missions

- Add pagination_next to React quickstart to fix "Next" button linking to itself
- Fix Rust type table: Duration → TimeDuration to match example code
- Fix TypeScript optional column indexing to use table-level syntax
Documents that module-level state does not persist across reducer calls
and that all persistent state must be stored in tables.
Covers inline binary storage, external storage with references,
and hybrid approaches for thumbnails. Includes practical examples
for all three server languages.
Adds a practical Quick Start example showing how to:
- Set up subscriptions
- Use row callbacks (onInsert, onDelete, onUpdate)
- Understand the subscription flow

Examples provided for TypeScript, C#, and Rust clients.
Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
Signed-off-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
… non-persistent

Explains the multiple reasons why relying on module-level state is
undefined: fresh instances, module updates, concurrent execution,
crash recovery, non-transactional updates, and replay safety.
- Change generate output path from ../client-unity/Assets/autogen to ../Assets/autogen
- Fix Rust directory references from server-rust to spacetimedb to match what spacetime init creates
- Update inline storage recommendations to reflect ~100MB capability
- Add pricing context ($1/GB) for choosing between inline and external storage
- Fix Unity tutorial generate command path (../Assets/autogen not ../client-unity/Assets/autogen)
- Fix Rust directory references from server-rust to spacetimedb
Strip volatile fields that change every run:
- Timestamps (started_at, finished_at)
- Local paths (work_dir_golden, work_dir_llm)
- Temp database names (golden_db, llm_db)
- Server port numbers from scorer notes

Also clear llm_output for passing tests (keep for failures for debugging).
Add documentation for calling between reducers and procedures:
- Add "Calling Reducers from Procedures" section showing how to invoke
  reducer functions within withTx blocks
- Add "Scheduling Procedures" section explaining how reducers can
  schedule procedures via schedule tables
- Add tip to schedule-tables.md clarifying procedure scheduling
- Wrap code snippets inside reducer functions
- Fix TypeScript: ctx.timestamp.microseconds → ctx.timestamp.microsSinceUnixEpoch
- Fix Rust: use std::time::Duration instead of spacetimedb::Duration

All examples verified to compile in TypeScript, Rust, and C#.
cloutiertyler and others added 4 commits January 22, 2026 18:24
Change UploadToS3 procedure to return string instead of Result<string, string>
since sum types cannot be used as procedure return types for client code generation.
Use exceptions for error handling instead of Result.Err.
Add missing C# tab showing how to generate pre-signed URLs for
client-side uploads and confirm uploads via reducer.
Wrap C# code snippets in public static partial class Module to match
the standard SpacetimeDB C# module pattern and ensure examples compile.
@cloutiertyler
Copy link
Contributor Author

/update-llm-benchmark

@clockwork-labs-bot
Copy link
Collaborator

LLM Benchmark Results (ci-quickfix)

Language Mode Category Tests Passed Task Pass %
Rust rustdoc_json basics 26/27 91.7% ⬆️ +8.3%
Rust rustdoc_json schema 21/34 58.7% ⬇️ -6.7%
Rust rustdoc_json total 47/61 76.7% ⬆️ +1.5%
Rust docs basics 5/27 11.1%
Rust docs schema 8/30 20.5% ⬆️ +5.0%
Rust docs total 13/57 15.4% ⬆️ +2.3%
C# docs basics 27/27 100.0%
C# docs schema 19/32 60.3% ⬇️ -3.3%
C# docs total 46/59 82.0% ⬇️ -1.5%

Compared against master branch baseline

Generated at: 2026-01-22T23:45:28.101Z

Failure Analysis (click to expand)

Benchmark Failure Analysis

Generated from: /__w/SpacetimeDB/SpacetimeDB/tools/xtask-llm-benchmark/../../docs/llms/docs-benchmark-details.json

Summary

  • Total failures analyzed: 34

SpacetimeDB Benchmark Test Failures Analysis

This document analyzes the SpacetimeDB benchmark test failures. Each failure is broken down into its components, specifying the discrepancies between the generated code and the expected golden examples. Specific, actionable recommendations for documentation adjustments are provided to avoid such issues in the future.

Rust / rustdoc_json Failures

1. General Errors with Code Generation

Failures Overview: The following tests failed due to various issues regarding table definitions, missing pub keywords, and improper syntax in function signatures.

Failures Grouped by Cause


a. Issue with pub Keyword and Function Signatures

Failures:

  • t_002_scheduled_table
  • t_015_product_type_columns
  • t_017_scheduled_columns

1. t_002_scheduled_table

Error: publish_error: spacetime publish failed (exit=1)

LLM Output:

#[table(name = tick_timer, scheduled(reducer = tick, column = scheduled_at))]
pub struct TickTimer {
    #[primary_key]
    #[auto_inc]
    scheduled_id: u64,
    scheduled_at: ScheduleAt,
}

#[reducer(init)]
pub fn init(ctx: &ReducerContext) {
    ctx.db.tick_timer().insert(TickTimer {
        scheduled_id: 0,
        scheduled_at: ScheduleAt::AfterMicros(50_000),
    });
}

Expected:

#[table(name = tick_timer, scheduled(tick))]
pub struct TickTimer {
    #[primary_key]
    #[auto_inc]
    pub scheduled_id: u64,
    pub scheduled_at: ScheduleAt,
}

#[reducer]
pub fn tick(_ctx: &ReducerContext, _schedule: TickTimer) { }

#[reducer(init)]
pub fn init(ctx: &ReducerContext) {
    ctx.db.tick_timer().insert(TickTimer {
        scheduled_id: 0,
        scheduled_at: ScheduleAt::Interval(Duration::from_millis(50).into()),
    });
}

2. t_015_product_type_columns

Error: publish_error: spacetime publish failed (exit=1)

LLM Output:

#[derive(SpacetimeType)]
pub struct Address {
    street: String,
    zip: i32,
}

#[table(name = profile)]
pub struct Profile {
    #[primary_key]
    id: i32,
    home: Address,
    work: Address,
}

Expected:

#[derive(SpacetimeType, Clone, Debug)]
pub struct Address {
    pub street: String,
    pub zip: i32,
}

#[table(name = profile)]
pub struct Profile {
    #[primary_key]
    pub id: i32,
    pub home: Address,
    pub work: Address,
}

3. t_017_scheduled_columns

Error: publish_error: spacetime publish failed (exit=1)

LLM Output:

#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]
pub struct TickTimer {
    #[primary_key]
    #[auto_inc]
    scheduled_id: u64,
    scheduled_at: ScheduleAt,
}

Expected:

#[table(name = tick_timer, scheduled(tick))]
pub struct TickTimer {
    #[primary_key]
    #[auto_inc]
    pub scheduled_id: u64,
    pub scheduled_at: ScheduleAt,
}

Explanation

  • Difference: The generated code often lacks the pub specifier on struct fields and functions, making them inaccessible outside their module. Function signatures also often miss the required return types.
  • Root Cause: Documentation may not clarify that all fields in a SpacetimeDB struct need to be public.
  • Recommendation: Update the documentation to specify that all fields in structs used in SpacetimeDB contexts should be marked as pub, along with specifying the necessity of return types for all reducer functions.

b. Errors Regarding Missing or Incorrect Implementation

Failures:

  • t_013_spacetime_sum_type
  • t_016_sum_type_columns
  • t_018_constraints

1. t_013_spacetime_sum_type

Error: no such table: result.

LLM Output:

#[table(name = result)]
pub struct ResultRow {
    #[primary_key]
    id: i32,
    value: Shape,
}

Expected:

#[table(name = result)]
pub struct ResultRow {
    #[primary_key]
    pub id: i32,
    pub value: Shape,
}

2. t_016_sum_type_columns

Error: no such table: drawings.

LLM Output:

#[spacetimedb::table(name = drawing)]
pub struct Drawing {
    #[primary_key]
    id: i32,
    a: Shape,
    b: Shape,
}

Expected:

#[table(name = drawing)]
pub struct Drawing {
    #[primary_key]
    pub id: i32,
    pub a: Shape,
    pub b: Shape,
}

3. t_018_constraints

Error: no such table: account.

LLM Output:

#[table(name = account, index(name = by_name, btree(columns = [name])))]
pub struct Account {
    #[primary_key]
    id: i32,
    #[unique]
    email: String,
    name: String,
}

Expected:

#[table(
    name = account,
    index(name = by_name, btree(columns = [name]))
)]
pub struct Account {
    #[primary_key]
    pub id: i32,
    #[unique]
    pub email: String,
    pub name: String,
}

Explanation

  • Difference: The generated code frequently omits the pub visibility and fails to implement the required structure correctly.
  • Root Cause: Documentation may not sufficiently outline the importance of proper struct and field visibility in SpacetimeDB schemas.
  • Recommendation: Emphasize in the documentation that all fields in structs must be public and provide examples highlighting this requirement.

Rust / docs Failures

Timeout Issues

Failure

  • t_020_ecs

Analysis

This failure indicates that the task exceeded its time limit during execution. Check if the test could be optimized for performance or if the complexity is overly high.

Recommendation

Optimize the performance of tests, ensuring they do not exceed reasonable time limits. Additionally, incorporate timeouts into the documentation to assist developers in recognizing performance expectations.


C# / docs Failures

1. Missing pub Keywords and Related Language Features


Failures:

  • t_014_elementary_columns
  • t_017_scheduled_columns

1. t_014_elementary_columns

Error: no such table: primitive.

LLM Output:

[Table(Name = "Primitive", Public = true)]
public partial struct Primitive
{
    [PrimaryKey]
    public int Id;
    public int Count;
    public long Total;
    public float Price;
    public double Ratio;
    public bool Active;
    public string Name;
}

Expected:

[Table(Name = "Primitive")]
public partial struct Primitive
{
    [PrimaryKey] public int Id;
    public int Count;
    public long Total;
    public float Price;
    public double Ratio;
    public bool Active;
    public string Name;
}

2. t_017_scheduled_columns

Error: no such table: tick_timer.

LLM Output:

[Table(Name = "TickTimer", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]
public partial struct TickTimer
{
    [PrimaryKey, AutoInc]
    public ulong ScheduledId;
    public ScheduleAt ScheduledAt;
}

Expected:

[Table(Name = "TickTimer", Scheduled = nameof(Tick), ScheduledAt = nameof(ScheduledAt))]
public partial struct TickTimer
{
    [PrimaryKey, AutoInc] public ulong ScheduledId;
    public ScheduleAt ScheduledAt;
}

Explanation

  • Difference: The generated C# code fails to keep all relevant visibility concerns in mind.
  • Root Cause: Similar to Rust failures, the need for public visibility in C# struct fields hasn't been emphasized, leading to errors.
  • Recommendation: Document the need for public access modifiers explicitly for struct fields in C#, especially for types used with SpacetimeDB.

Conclusion

This analysis provides insights into the root causes of the failures in the SpacetimeDB benchmarks across both Rust and C#. Identifying the missing pub keywords, incorrect function signatures, and general visibility issues will help refine documentation and improve the quality of generated code. By adopting the recommended changes, future tests will likely yield more consistent successes.

@cloutiertyler cloutiertyler added this pull request to the merge queue Jan 23, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 23, 2026
@cloutiertyler
Copy link
Contributor Author

/update-llm-benchmark

@clockwork-labs-bot
Copy link
Collaborator

LLM Benchmark Results (ci-quickfix)

Language Mode Category Tests Passed Task Pass %
Rust rustdoc_json basics 25/27 83.3% ⬆️ +6.9%
Rust rustdoc_json schema 23/34 65.3% ⬇️ -10.0%
Rust rustdoc_json total 48/61 75.2% ⬇️ -0.8%
Rust docs basics 5/27 11.1%
Rust docs schema 8/34 20.5%
Rust docs total 13/61 15.4%
C# docs basics 27/27 100.0%
C# docs schema 25/34 73.7% ⬆️ +10.0%
C# docs total 52/61 88.0% ⬆️ +4.5%

Compared against master branch baseline

Generated at: 2026-01-27T17:31:13.034Z

Failure Analysis (click to expand)

Benchmark Failure Analysis

Generated from: /__w/SpacetimeDB/SpacetimeDB/tools/xtask-llm-benchmark/../../docs/llms/docs-benchmark-details.json

Summary

  • Total failures analyzed: 34

Analysis of SpacetimeDB Benchmark Test Failures

Rust / rustdoc_json Failures

Compile/Publish Errors

t_002_scheduled_table and t_017_scheduled_columns

1. The generated code:

use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};

#[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]
pub struct TickTimer {
    #[primary_key]
    #[auto_inc]
    scheduled_id: u64,
    scheduled_at: ScheduleAt,
}

#[reducer(init)]
pub fn init(ctx: &ReducerContext) {
    if ctx.db.tick_timer().count() == 0 {
        ctx.db.tick_timer().insert(TickTimer {
            scheduled_id: 0,
            scheduled_at: ScheduleAt::repeat_micros(50_000),
        });
    }
}

#[reducer]
pub fn tick(_ctx: &ReducerContext, _scheduled: TickTimer) {}

2. The golden example:

use spacetimedb::{reducer, table, ReducerContext, ScheduleAt, Table};
use std::time::Duration;

#[table(name = tick_timer, scheduled(tick))]
pub struct TickTimer {
    #[primary_key]
    #[auto_inc]
    pub scheduled_id: u64,
    pub scheduled_at: ScheduleAt,
}

#[reducer]
pub fn tick(_ctx: &ReducerContext, _schedule: TickTimer) {
}

#[reducer(init)]
pub fn init(ctx: &ReducerContext) {
    let every_50ms: ScheduleAt = Duration::from_millis(50).into();
    ctx.db.tick_timer().insert(TickTimer {
        scheduled_id: 0,
        scheduled_at: every_50ms,
    });
}

3. The error:
publish_error: spacetime publish failed (exit=1)

4. Explain the difference:
The LLM-generated code uses ScheduleAt::repeat_micros(50_000) while the golden example uses ScheduleAt::Interval(Duration::from_millis(50).into()), which is the correct way to specify a scheduling interval.

5. Root cause:
The documentation may not clearly explain the necessary type conversions for ScheduleAt, specifically the use of Duration.

6. Recommendation:
Update the documentation to specify that ScheduleAt should use Duration for intervals. Provide clear examples demonstrating the correct usage.


Other Failures

t_003_struct_in_table

1. The generated code:

use spacetimedb::{table, reducer, ReducerContext, SpacetimeType};

#[derive(SpacetimeType)]
pub struct Position {
    x: i32,
    y: i32,
}

#[table(name = entity)]
pub struct Entity {
    #[primary_key]
    id: i32,
    pos: Position,
}

#[reducer(init)]
pub fn init(_ctx: &ReducerContext) {}

2. The golden example:

use spacetimedb::{table, SpacetimeType};

#[derive(SpacetimeType, Clone, Debug)]
pub struct Position {
    pub x: i32,
    pub y: i32,
}

#[table(name = entity)]
pub struct Entity {
    #[primary_key]
    pub id: i32,
    pub pos: Position,
}

3. The error:
schema_parity: reducers differ - expected [], got ["init()"]

4. Explain the difference:
The LLM-generated code references a reducer (init) that does not match the expected structure of the table, which requires the init function to have a return type conforming to Result<(), String>.

5. Root cause:
The documentation does not make it clear that all reducers need to behave consistently with input/signatures.

6. Recommendation:
Clarify in the documentation that the reducer functions must follow certain patterns and signatures, especially with regard to return types.


Sum Type Failures

t_013_spacetime_sum_type and t_015_product_type_columns

1. The generated code:

use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType, UniqueColumn};

#[derive(SpacetimeType)]
pub struct Rect {
    width: i32,
    height: i32,
}

#[derive(SpacetimeType)]
pub enum Shape {
    Circle(i32),
    Rectangle(Rect),
}

#[table(name = result)]
pub struct ResultRow {
    #[primary_key]
    id: i32,
    value: Shape,
}

#[reducer]
pub fn set_circle(ctx: &ReducerContext, id: i32, radius: i32) {
    ctx.db.result().id().delete(&id);
    ctx.db.result().insert(ResultRow {
        id,
        value: Shape::Circle(radius),
    });
}

2. The golden example:

use spacetimedb::{reducer, table, ReducerContext, SpacetimeType, Table};

#[derive(SpacetimeType, Clone, Debug)]
pub struct Rect {
    pub width: i32,
    pub height: i32,
}

#[derive(SpacetimeType, Clone, Debug)]
pub enum Shape {
    Circle(i32),
    Rectangle(Rect),
}

#[table(name = result)]
pub struct ResultRow {
    #[primary_key]
    pub id: i32,
    pub value: Shape,
}

#[reducer]
pub fn set_circle(ctx: &ReducerContext, id: i32, radius: i32) {
    ctx.db.result().insert(ResultRow { id, value: Shape::Circle(radius) });
}

3. The error:
no such table: result or no such table: profile

4. Explain the difference:
The generated code omits declaring public visibility for the fields in the structs, which is necessary for SpacetimeDB to properly reflect the structure of the data.

5. Root cause:
Lack of clarity in the documentation regarding field visibility in structs.

6. Recommendation:
Enhance the documentation to explicitly state that all struct fields used in SpacetimeDB must be pub, and provide examples to illustrate this requirement.


Constraints and Schema Issues

t_018_constraints

1. The generated code:

use spacetimedb::{table, reducer, ReducerContext, Table};

#[table(name = account, index(name = by_name, btree(columns = [name])))]
pub struct Account {
    #[primary_key]
    id: i32,
    #[unique]
    email: String,
    name: String,
}

#[reducer]
pub fn seed(ctx: &ReducerContext) {
    ctx.db.account().insert(Account { id: 1, email: "a@example.com".to_string(), name: "Alice".to_string() });
    ctx.db.account().insert(Account { id: 2, email: "b@example.com".to_string(), name: "Bob".to_string() });
}

2. The golden example:

use spacetimedb::{reducer, table, ReducerContext, Table};

#[table(
    name = account,
    index(name = by_name, btree(columns = [name]))
)]
pub struct Account {
    #[primary_key]
    pub id: i32,
    #[unique]
    pub email: String,
    pub name: String,
}

#[reducer]
pub fn seed(ctx: &ReducerContext) {
    ctx.db.account().insert(Account { id: 1, email: "a@example.com".into(), name: "Alice".into() });
    ctx.db.account().insert(Account { id: 2, email: "b@example.com".into(), name: "Bob".into() });
}

3. The error:
no such table: account

4. Explain the difference:
The generated code lacks pub visibility for the fields in the Account struct and fails to use .into() for string initialization.

5. Root cause:
Documentation does not emphasize the importance of field visibility and conversions when using SpacetimeDB.

6. Recommendation:
Update the documentation to clarify that struct fields must be public and suggest correct practices for type conversions, including .into() for string initialization.


C# / docs Failures

Scheduled Columns Failures

t_016_sum_type_columns and t_017_scheduled_columns

1. The generated code:

using SpacetimeDB;

public static partial class Module
{
    [SpacetimeDB.Type]
    public partial struct Circle
    {
        public int Radius;
    }

    [SpacetimeDB.Type]
    public partial struct Rectangle
    {
        public int Width;
        public int Height;
    }

    [SpacetimeDB.Type]
    public partial record Shape : TaggedEnum<(
        Circle Circle,
        Rectangle Rectangle
    )> { }

    [SpacetimeDB.Table(Name = "Drawing", Public = true)]
    public partial struct Drawing
    {
        [SpacetimeDB.PrimaryKey]
        public int Id;
        public Shape A;
        public Shape B;
    }
}

2. The golden example:

using SpacetimeDB;

public static partial class Module
{
    [Type]
    public partial struct Circle { public int Radius; }

    [Type]
    public partial struct Rectangle { public int Width; public int Height; }

    [Type]
    public partial record Shape : TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }

    [Table(Name = "Drawing")]
    public partial struct Drawing
    {
        [PrimaryKey] public int Id;
        public Shape A;
        public Shape B;
    }
}

3. The error:
no such table: drawings

4. Explain the difference:
The generated code lacks public access modifiers for structs, which leads to conflicts with the accessibility required when accessing tables.

5. Root cause:
Insufficient explanation in the documentation about the required access modifiers for types used with SpacetimeDB.

6. Recommendation:
Add more guidance in the documentation on the necessity of public access modifiers for types to ensure proper table access.


This comprehensive analysis highlights the specific sinks, root causes, and actionable recommendations to improve both the SpacetimeDB documentation and the generated code's adherence to expected standards.

@cloutiertyler cloutiertyler added this pull request to the merge queue Jan 27, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to a conflict with the base branch Jan 27, 2026
@bfops
Copy link
Collaborator

bfops commented Jan 27, 2026

/update-llm-benchmark

@clockwork-labs-bot
Copy link
Collaborator

LLM Benchmark Results (ci-quickfix)

Language Mode Category Tests Passed Task Pass %
Rust rustdoc_json basics 26/27 91.7% ⬆️ +8.3%
Rust rustdoc_json schema 23/34 65.3% ⬆️ +10.0%
Rust rustdoc_json total 49/61 79.7% ⬆️ +9.1%
Rust docs basics 5/27 11.1%
Rust docs schema 8/32 20.5% ⬆️ +8.0%
Rust docs total 13/59 15.4% ⬆️ +3.6%
C# docs basics 27/27 100.0%
C# docs schema 25/34 73.7%
C# docs total 52/61 88.0%

Compared against master branch baseline

Generated at: 2026-01-27T20:01:19.767Z

Failure Analysis (click to expand)

Benchmark Failure Analysis

Generated from: /__w/SpacetimeDB/SpacetimeDB/tools/xtask-llm-benchmark/../../docs/llms/docs-benchmark-details.json

Summary

  • Total failures analyzed: 33

Analysis of SpacetimeDB Benchmark Test Failures

Rust / rustdoc_json Failures

Compile/Publish Errors (2 Failures)

Failure Group 1: t_002_scheduled_table and t_017_scheduled_columns
  1. The generated code:

    use spacetimedb::{table, reducer, ReducerContext, Table, ScheduleAt};
    
    #[table(name = tick_timer, schedule(reducer = tick, column = scheduled_at))]
    pub struct TickTimer {
        #[primary_key]
        #[auto_inc]
        scheduled_id: u64,
        scheduled_at: ScheduleAt,
    }
    
    #[reducer(init)]
    pub fn init(ctx: &ReducerContext) {
        if ctx.db.tick_timer().count() == 0 {
            ctx.db.tick_timer().insert(TickTimer {
                scheduled_id: 0,
                scheduled_at: ScheduleAt::RepeatMicros(50_000),
            });
        }
    }
    
    #[reducer]
    pub fn tick(_ctx: &ReducerContext, _row: TickTimer) {
    }
  2. The golden example:

    use spacetimedb::{reducer, table, ReducerContext, ScheduleAt, Table};
    use std::time::Duration;
    
    #[table(name = tick_timer, scheduled(tick))]
    pub struct TickTimer {
        #[primary_key]
        #[auto_inc]
        pub scheduled_id: u64,
        pub scheduled_at: ScheduleAt,
    }
    
    #[reducer]
    pub fn tick(_ctx: &ReducerContext, _schedule: TickTimer) {
    }
    
    #[reducer(init)]
    pub fn init(ctx: &ReducerContext) {
        let every_50ms: ScheduleAt = Duration::from_millis(50).into();
        ctx.db.tick_timer().insert(TickTimer {
            scheduled_id: 0,
            scheduled_at: every_50ms,
        });
    }
  3. The error:

    • publish_error: spacetime publish failed (exit=1)
  4. Explain the difference:

    • The generated code used ScheduleAt::RepeatMicros(50_000) instead of the correct ScheduleAt::Interval(Duration::from_millis(50).into()). The way the scheduling was set up was incorrect.
  5. Root cause:

    • The documentation does not clearly specify the constructor syntax for ScheduleAt nor how to correctly set up the scheduled tasks in this context.
  6. Recommendation:

    • Update documentation to provide examples of different constructors for ScheduleAt, specifically emphasizing how to define intervals correctly.

Other Failures (5 failures)

Failure Group 2: t_013_spacetime_sum_type, t_015_product_type_columns, t_016_sum_type_columns, t_018_constraints, t_020_ecs
  1. The generated code:

    use spacetimedb::{table, reducer, ReducerContext, Table, SpacetimeType};
    
    #[derive(SpacetimeType)]
    pub struct Rect {
        width: i32,
        height: i32,
    }
    
    #[table(name = result)]
    pub struct ResultRow {
        #[primary_key]
        id: i32,
        value: Shape,
    }
    
    #[reducer]
    pub fn set_circle(ctx: &ReducerContext, id: i32, radius: i32) {
        ctx.db.result().insert(ResultRow {
            id,
            value: Shape::Circle(radius),
        });
    }
  2. The golden example:

    use spacetimedb::{reducer, table, ReducerContext, SpacetimeType, Table};
    
    #[derive(SpacetimeType, Clone, Debug)]
    pub struct Rect {
        pub width: i32,
        pub height: i32,
    }
    
    #[table(name = result)]
    pub struct ResultRow {
        #[primary_key]
        pub id: i32,
        pub value: Shape,
    }
    
    #[reducer]
    pub fn set_circle(ctx: &ReducerContext, id: i32, radius: i32) {
        ctx.db.result().insert(ResultRow { id, value: Shape::Circle(radius) });
    }
  3. The error:

    • spacetime sql failed: no such table: result
    • spacetime sql failed: no such table: profile
    • spacetime sql failed: no such table: drawings
  4. Explain the difference:

    • The generated code omits the pub visibility keyword for fields and structs, which prevents proper access by the macros that generate the expected database schema. Additionally, the enum Shape wasn't declared correctly in the generated code.
  5. Root cause:

    • Lack of proper visibility (missing pub) for structs and enum fields was not clearly emphasized in the documentation, leading to access issues.
  6. Recommendation:

    • Provide clear guidelines in the documentation regarding the necessity of using pub for struct and enum fields when working with SpacetimeDB components. Include example schemas with visibility marked.

Rust / docs Failures (22 total)

Timeout Issues (1 failure)

  1. Failure Group: t_015_product_type_columns
    • Expected: Modify the query logic to ensure no unnecessary long-running operations exist.
    • Recommendation: Provide timeout considerations in the documentation to ensure optimization options are explored to prevent long-running tasks.

C# / docs Failures (4 total)

C# Failure Group: t_014_elementary_columns, t_016_sum_type_columns, t_017_scheduled_columns, t_020_ecs

  1. The generated code:

    using SpacetimeDB;
    
    public static partial class Module
    {
        [SpacetimeDB.Table(Name = "Primitive", Public = true)]
        public partial struct Primitive
        {
            [SpacetimeDB.PrimaryKey]
            public int Id;
            public int Count;
            public long Total;
            public float Price;
            public double Ratio;
            public bool Active;
            public string Name;
        }
    
        [SpacetimeDB.Reducer]
        public static void Seed(ReducerContext ctx)
        {
            ctx.Db.Primitive.Insert(new Primitive
            {
                Id = 1,
                Count = 2,
                Total = 3000000000L,
                Price = 1.5f,
                Ratio = 2.25,
                Active = true,
                Name = "Alice"
            });
        }
    }
  2. The golden example:

    using SpacetimeDB;
    
    public static partial class Module
    {
        [Table(Name = "Primitive")]
        public partial struct Primitive
        {
            [PrimaryKey] public int Id;
            public int Count;
            public long Total;
            public float Price;
            public double Ratio;
            public bool Active;
            public string Name;
        }
    
        [Reducer]
        public static void Seed(ReducerContext ctx)
        {
            ctx.Db.Primitive.Insert(new Primitive {
                Id = 1,
                Count = 2,
                Total = 3000000000,
                Price = 1.5f,
                Ratio = 2.25,
                Active = true,
                Name = "Alice"
            });
        }
    }
  3. The error: no such table: primitive

  4. Explain the difference:

    • Missing the public access modifier in the declaration of the Table attribute. The expected syntax properly utilizes attributes defined in the library.
  5. Root cause:

    • Documentation may lack clarity about access modifiers, especially when it comes to how they affect visibility in entities.
  6. Recommendation:

    • Ensure C# documentation includes explicit examples where public is required in class and struct declarations to prevent access issues with tables.

By addressing the above gaps in documentation and ensuring that generated samples adhere to the expected outcomes, we can significantly reduce the number of failures in future benchmarks.

@cloutiertyler cloutiertyler added this pull request to the merge queue Jan 27, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 27, 2026
@bfops bfops added this pull request to the merge queue Jan 27, 2026
github-merge-queue bot pushed a commit that referenced this pull request Jan 27, 2026
# Description of Changes

  Two documentation improvements:

1. **Reducers documentation**: Clarified that using global/static
variables in reducers is **undefined behavior**, not just "values won't
persist". Added six specific reasons why this is undefined:
     - Fresh execution environments
     - Module updates
     - Concurrent execution
     - Crash recovery
     - Non-transactional updates
     - Replay safety

2. **Access permissions documentation**: Replaced the "Combining Both
Techniques" example that used indexes on Option fields (which
SpacetimeDB doesn't support) with a working example that filters by a
required `department` field instead.

  # API and ABI breaking changes

  None. Documentation only.

  # Expected complexity level and risk

  1 - Documentation changes only.

  # Testing

  - [ ] Verify the reducers warning is clear and accurate
  - [ ] Verify the access permissions example compiles and makes sense

---------

Signed-off-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com>
Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 27, 2026
@bfops bfops added this pull request to the merge queue Jan 27, 2026
Merged via the queue into master with commit 504b13b Jan 27, 2026
27 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants