Skip to content

Rust mode can only be activated once: second activation fails with UNIQUE constraint failed: mc_workspaces.name #390

Description

@iceteaSA

Summary

transform_mode: "rust" works on a store that has never had it enabled. On any subsequent activation, every pass fails in host-side state_sync with UNIQUE constraint failed: mc_workspaces.name, because the authority workspace row from the first activation is still there and the insert has no conflict handling.

The module is never reached (module=0.0 ms), so it fails closed — but it fails on every pass, permanently, until the row is removed by hand.

Evidence

Every pass after enabling:

rust pass: decision=error reason=none served_from=raw in=417 out=417 applied=false
           row_version=0 elapsed=2386.1 ms module=0.0 ms
           stages=prefix_guard:0.0 ordinal_resolve:65.6 state_sync:2053.0 clone:0.0 wire_build:0.0
lkg_miss
rust transform failed; attempting LKG replay:
    store: store: storage backend: UNIQUE constraint failed: mc_workspaces.name

All the elapsed time is in state_sync; module=0.0 ms confirms dispatch never happens.

The offending row, written by the first activation:

id  name                                                     created_at  updated_at
1   authority-workspace-aa195aa5…-8cebe0c8…                  0           0

Root cause

crates/mc-store/src/lib.rs has two inserts into mc_workspaces. Only one is idempotent.

// :14066 — safe
INSERT INTO mc_workspaces (name, share_categories) VALUES (?1, ?2)
  ON CONFLICT(name) DO NOTHING

// :15608 — the activation path, no conflict handling
INSERT INTO mc_workspaces (name, created_at, updated_at, share_categories)
  VALUES (?1, 0, 0, ?2)
let workspace_id = tx.last_insert_rowid();

mc_workspaces.name is TEXT NOT NULL UNIQUE. The literal 0, 0 in the second statement matches the created_at=0, updated_at=0 on the live row, which is how I attributed it to that path.

Note the follow-on: last_insert_rowid() immediately after means a bare ON CONFLICT DO NOTHING is not sufficient — the fix needs get-or-create semantics returning the existing id (ON CONFLICT(name) DO UPDATE SET name=excluded.name RETURNING id, or a select-then-insert).

Reproduction

  1. Enable transform_mode: "rust", restart, let at least one pass run (this seeds mc_workspaces).
  2. Revert to ts, restart.
  3. Re-enable rust, restart.

Every pass now errors as above. Deleting the mc_workspaces row restores a working first-activation.

Two smaller things noticed alongside

decision=error reason=none. The pass line carries an empty reason while the actual cause (UNIQUE constraint failed) is only in a separate rust transform failed; attempting LKG replay: line. Propagating the store error into reason= would make the pass line self-diagnosing.

Project-tier subc is stripped, and the message is good. Setting subc.connection_file in a project config is silently ignored (correctly — project-security.ts:415), and rust mode declines with rust mode requires user-level subc configuration; running ts. That diagnostic named the fix exactly; no complaint, noting it because the working split is user-tier subc + project-tier transform_mode, which isn't obvious from the config docs.

Environment

magic-context fork on upstream master (all of #376/#378/#379/#381 present), ck-mc rebuilt at matching generation (1160 tests pass). Live store: schema v82, ~3200 memories. Fails identically on every pass across four restarts.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions