Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/lina-core/src/agents/persona-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,11 @@ function profileFor(
if (identity === null) return null;
for (const profile of parseIdentityPolicy(identity).profiles)
if (profile.agentId === agentId) return profile;
return null;
// A supplied snapshot is a claim about every participant, so a missing target
// is bad data, not an absent policy. Collapsing it to null would unlock every
// axis and produce the same digest as identity: null. The LIFE paths already
// reject this: world/views.ts, world/growth.ts, world/autonomy-rules.ts.
throw Error("Missing persona identity policy");
}

function lockedFor(
Expand Down
72 changes: 53 additions & 19 deletions packages/lina-core/test/persona-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,27 +256,61 @@ test("derivation digest is idempotent and ignores v2-only identity fields", () =
const fromV2 = derive(identityV2);
expect(fromV1.digest).toBe(fromV2.digest);
expect(fromV1.digest).toBe(first.digest);
});

const missing = personaSchemaFromLifeDefinition({
agentId: "lina",
revision: 1,
definition,
identity: {
version: 2 as const,
profiles: [
{
...lockProfile,
agentId: "mira",
evolution: "manual",
personalBehavior: null,
sourceStamp: null,
},
],
},
test("supplied identity without the target agent is rejected", () => {
const before = derive();
const otherAgent = {
...lockProfile,
agentId: "mira",
evolution: "manual" as const,
personalBehavior: null,
sourceStamp: null,
};
for (const profiles of [[], [otherAgent]]) {
expect(() =>
personaSchemaFromLifeDefinition({
agentId: "lina",
revision: 1,
definition,
identity: { version: 2 as const, profiles },
}),
).toThrow("Missing persona identity policy");
}
expect(derive()).toEqual(before);
});

test("absent policy, manual lock and selective lock stay separate cases", () => {
const absent = derive(null);
expect(absent.sourceIdentity).toBeNull();
expect(absent.dimensions.every((row) => row.locked === false)).toBe(true);

const selective = derive();
expect(selective.sourceIdentity).toEqual({ profileRevision: 4 });
expect(selective.dimensions.map((row) => [row.id, row.locked])).toEqual([
["curiosity", false],
["warmth", true],
["tea", false],
["trust", false],
]);
expect(selective.digest).not.toBe(absent.digest);

const manual = derive({
version: 2 as const,
profiles: [
{
...lockProfile,
evolution: "manual" as const,
lockedTraitIds: [],
personalBehavior: null,
sourceStamp: null,
},
],
});
expect(missing.sourceIdentity).toBeNull();
expect(missing.dimensions.every((row) => row.locked === false)).toBe(true);
expect(missing.digest).toBe(unlocked.digest);
expect(manual.dimensions.every((row) => row.locked === true)).toBe(true);
expect(new Set([absent.digest, selective.digest, manual.digest]).size).toBe(
3,
);
});

test.each([identityV1, identityV2])(
Expand Down
Loading