You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
user_type === "mind" is used throughout the codebase as a proxy for "is this actor a mind," but it does not mean that. The spirit's user row is user_type: "system", and bridge puppets are "puppet". So the check silently excludes both — and whether that is correct depends entirely on what the call site meant, which nothing records.
The result is a check that is wrong by default and right by accident, and it keeps costing us:
packages/extensions/intentions/src/routes.ts:163 carries a comment warning "Do NOT use user_type === "mind" as a proxy" — someone hit this, understood it, and left a warning for the next person instead of a fix. That comment is the clearest evidence the primitive is wrong rather than the callers being careless.
Scale
Roughly 30 call sites compare user_type/userType against a string literal across packages/web, packages/cli, packages/daemon, and packages/extensions. Each is independently a judgment about whether the spirit and puppets belong, and there is no way to tell a deliberate exclusion from an accidental one by reading it.
The two unions disagree
packages/api/src/types.ts:
:95 userType: "human"|"mind"|"puppet"|"system";
:228 user_type: "human"|"mind"|"system";// no "puppet"
Four values are actually written to the DB (human, mind, puppet, system), so the narrower union is simply wrong, and a puppet flowing through it type-checks as something it isn't.
packages/daemon/src/lib/schema.ts:39 types the column as plain text with a "human" default, so the database enforces nothing.
Why this recurs
user_type is carrying at least three different questions at once:
Is this actor an agent rather than a person? (spirit: yes. puppet: sort of — it stands in for a person.)
Does this actor have a local mind directory / registry row? (spirit: yes. external minds: no.)
How should this actor be rendered? (spirit wants its own icon; puppets currently render as humans.)
Call sites want different ones of these and all reach for the same field. packages/web/src/ui/lib/user-kind.ts already exists as a partial answer for the frontend, which suggests the right shape — it just isn't shared, isn't complete, and isn't used by the ~30 raw comparisons.
Suggested direction
Not prescriptive, but the shape that seems indicated:
Named predicates expressing the actual question (isMindLike, hasLocalMindDir, isAgentActor — whatever the real distinctions turn out to be), shared across daemon/web/CLI/extensions rather than reimplemented per package.
Reconcile the two unions and make the schema type the column as the union rather than free text.
Migrate the raw comparisons to the predicates, so a deliberate exclusion of the spirit reads as deliberate.
The goal is that the next person writing a guard cannot accidentally exclude the spirit without saying so.
Context
Surfaced during #807 (Notes/Pages merge). Related: #786.
Problem
user_type === "mind"is used throughout the codebase as a proxy for "is this actor a mind," but it does not mean that. The spirit's user row isuser_type: "system", and bridge puppets are"puppet". So the check silently excludes both — and whether that is correct depends entirely on what the call site meant, which nothing records.The result is a check that is wrong by default and right by accident, and it keeps costing us:
user_type !== "mind"guard).packages/extensions/intentions/src/routes.ts:163carries a comment warning "Do NOT useuser_type === "mind"as a proxy" — someone hit this, understood it, and left a warning for the next person instead of a fix. That comment is the clearest evidence the primitive is wrong rather than the callers being careless.Scale
Roughly 30 call sites compare
user_type/userTypeagainst a string literal acrosspackages/web,packages/cli,packages/daemon, andpackages/extensions. Each is independently a judgment about whether the spirit and puppets belong, and there is no way to tell a deliberate exclusion from an accidental one by reading it.The two unions disagree
packages/api/src/types.ts:Four values are actually written to the DB (
human,mind,puppet,system), so the narrower union is simply wrong, and a puppet flowing through it type-checks as something it isn't.packages/daemon/src/lib/schema.ts:39types the column as plaintextwith a"human"default, so the database enforces nothing.Why this recurs
user_typeis carrying at least three different questions at once:Call sites want different ones of these and all reach for the same field.
packages/web/src/ui/lib/user-kind.tsalready exists as a partial answer for the frontend, which suggests the right shape — it just isn't shared, isn't complete, and isn't used by the ~30 raw comparisons.Suggested direction
Not prescriptive, but the shape that seems indicated:
isMindLike,hasLocalMindDir,isAgentActor— whatever the real distinctions turn out to be), shared across daemon/web/CLI/extensions rather than reimplemented per package.The goal is that the next person writing a guard cannot accidentally exclude the spirit without saying so.
Context
Surfaced during #807 (Notes/Pages merge). Related: #786.