MemberJunction / MemberJunction/MJ

Eight MJ core List fields have a SQL default that is not in their own value list — six violate their own CHECK constraint

Open
#3,985 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TSQL
Stars
29
Forks
6
Avg merge
2d 1h
Merged PRs (30d)
323

Description

## Summary

Eight `ValueListType='List'` fields in MJ core have a SQL **default** that is not a member of their own value list. Six of them are not members of the underlying **CHECK constraint** either, so a create that relies on the default is refused by SQL Server outright; the other two match only by case.

This has been invisible because MJ's create stored procedures always pass the column, so the database-side default rarely fires — and because nothing on the TypeScript side compared a field's value against its value list until #3969. With that rung in place (PR #3972, merged), a `NewRecord()` that leaves one of these fields at its seeded default now fails `Validate()` with a field-named message instead of reaching SQL Server and coming back as a raw constraint violation. That is a strict improvement in the message, but it means this is where these stale defaults will start being noticed.

- Status: OPEN
- Found: 2026-08-20, while addressing review on PR #3972 (MJ 6.1.0-edge.2)
- Layer: **schema / metadata** — the column defaults (and in two cases the value itself), not `@memberjunction/core`

## The eight fields

Read from a current 6.1 instance database (`__mj.EntityField.DefaultValue` normalized the way `ExtractActualDefaultValue` does at populate time, so the runtime default really is the bare string shown):

| Entity | Field | Default | Value list | Matches list |
| --- | --- | --- | --- | --- |
| MJ: Queue Tasks | Status | `Pending` | In Progress \| Completed \| Failed | ✗ not at all |
| MJ: Audit Logs | Status | `Allow` | Success \| Failed | ✗ not at all |
| MJ: Authorization Roles | Type | `grant` | Allow \| Deny | ✗ not at all |
| MJ: Record Merge Logs | ProcessingStatus | `Pending` | Complete \| Started \| Error | ✗ not at all |
| MJ: Version Installations | Type | `System` | New \| Upgrade | ✗ not at all |
| MJ: Conversation Details | Role | `user_name()` | AI \| User \| Error | ✗ not at all |
| MJ: Entity AI Actions | TriggerEvent | `After Save` | before save \| after save | ~ case only |
| MJ: Entity AI Actions | OutputType | `FIeld` | entity \| field | ~ case only (**and misspelled**) |

The first six are genuine database contradictions, confirmed directly against `sys.check_constraints` / `sys.default_constraints`:

```
QueueTask.Status CHECK ([Status]='In Progress' OR [Status]='Completed' OR [Status]='Failed') DEFAULT (N'Pending')
AuditLog.Status CHECK ([Status]='Success' OR [Status]='Failed') DEFAULT (N'Allow')
VersionInstallation.Type CHECK ([Type]='New' OR [Type]='Upgrade') DEFAULT (N'System')
```

An `INSERT` that omits such a column is rejected by its own CHECK. The value lists are not stale metadata: CodeGen derives them from these constraints, so list and CHECK agree — it is the default that disagrees with both.

`MJ: Entity AI Actions`.OutputType also carries a **typo in the default** (`FIeld`, capital I), and both of that entity's defaults sit outside the union its own generated entity subclass declares, so a typed read returns a value the type says is impossible.

## Repro — how to recreate

```ts
const md = new Metadata();
const t = await md.GetEntityObject('MJ: Queue Tasks', user);
t.NewRecord(); // Status is seeded from DefaultValue -> 'Pending'
// ...set the other required fields...
await t.Save();
```

Before PR #3972: SQL Server rejects it — `The INSERT statement conflicted with the CHECK constraint`, no field named.
After PR #3972: `Validate()` fails first with `Status must be one of: In Progress, Completed, Failed. Current value is 'Pending'`.

Either way the create cannot succeed at the default. Pure SQL shows the same thing with no MJ involved:

```sql
INSERT INTO __mj.QueueTask (QueueID, Status) VALUES ('', DEFAULT); -- fails the CHECK
```

## Expected vs actual

**Expected** — a column's default is one of the values its CHECK constraint permits, so a create that omits the column succeeds and the value list, the default and the generated union all agree.

**Actual** — six defaults are rejected by their own constraint; two differ from their value list by case; one is misspelled.

## Suggested fix

A migration per field, but each needs a **product decision** rather than a mechanical rewrite, because either side could be the wrong one:

1. **`MJ: Queue Tasks`.Status** — is `Pending` a legitimate state that belongs in the CHECK (a task queued but not started), or should the default be `In Progress`? The name suggests the former; the constraint says the latter.
2. **`MJ: Audit Logs`.Status** (`Allow` vs Success|Failed) and **`MJ: Authorization Roles`.Type** (`grant` vs Allow|Deny) look like defaults left behind by a rename of the value vocabulary — likely fix the default.
3. **`MJ: Record Merge Logs`.ProcessingStatus** (`Pending` vs Complete|Started|Error) — same question as (1).
4. **`MJ: Version Installations`.Type** (`System` vs New|Upgrade) — a value that no longer exists; pick the intended one.
5. **`MJ: Conversation Details`.Role** — the default is `user_name()`, a SQL *function* rather than a literal, against a list of AI|User|Error. Almost certainly meant to be the literal `User`.
6. **`MJ: Entity AI Actions`.TriggerEvent / .OutputType** — align the case (and fix `FIeld`). Worth deciding which casing is canonical, since the generated union takes its values from the list while the default supplies the other casing.

Worth doing in one pass, since the diagnosis and the shape of the fix are identical across all eight.

## Related

- #3969 / PR #3972 — the value-list validation rung that makes these visible. Its comparison is deliberately case-insensitive **because** of the two `MJ: Entity AI Actions` fields; fixing their case would remove that dependency (the case-insensitivity would stay for SQL Server collation reasons).
- #3978 — numeric/bit `IN (…)` lists produce no value list at all.

Contributor guide

Open the contributing guide

Research direction

Start by comparing the eight listed columns with their sys.check_constraints and sys.default_constraints definitions, then review the corresponding value lists and generated entity unions. Resolve the product choice for each mismatch before preparing one migration per field. Done means every default is accepted by its CHECK constraint and agrees with the value list, including the Conversation Details function default and the Entity AI Actions typo and casing.

Written by the indexing model from the issue text.

Assessment

Tech stack
sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.