Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| The docs service answers every artifacts call: docs can be made, listed, shared, moved, trashed, restored, searched, versioned and edited live in their own rooms, agents read, write and recall them only where their person and everyone in the conversation can, and folio events go out on the bus, while Docs' pages keep working as before. | 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; | |
| 3 | ||
| 4 | import { aclChain, type FolioAclNode, type FolioGrant, type Person, type SpaceRules } from "../access.ts"; | |
| 5 | import { agentMayFind, agentReach, audienceRule, type AudienceRule } from "./agents.ts"; | |
| 6 | ||
| 7 | // The leak rules of docs/ARTIFACTS_MODE.md section 4.3. | |
| 8 | ||
| 9 | const ana: Person = { user_id: "ana", owner: false, teams: new Set(["web"]) }; | |
| 10 | const bo: Person = { user_id: "bo", owner: false, teams: new Set() }; | |
| 11 | const cy: Person = { user_id: "cy", owner: false, teams: new Set(["web"]) }; | |
| 12 | ||
| 13 | const open: SpaceRules = { kind: "workspace", team: null, default_role: "edit", members: [] }; | |
| 14 | const team: SpaceRules = { kind: "team", team: "web", default_role: "edit", members: [] }; | |
| 15 | ||
| 16 | function node(id: string, over: Partial<FolioAclNode> = {}): FolioAclNode { | |
| 17 | return { id, owner: "user:ana", parent_id: null, space_id: null, inherit: true, general_access: "none", general_role: null, ...over }; | |
| 18 | } | |
| 19 | ||
| 20 | function reach(n: FolioAclNode, grants: Record<string, FolioGrant[]>, space: SpaceRules | null, rule: AudienceRule, people: Person[] = [], visits: string[] = [], asker = ana) { | |
| 21 | return agentReach({ | |
| 22 | chain: aclChain(n.id, new Map([[n.id, n]])), | |
| 23 | grants: new Map(Object.entries(grants)), | |
| 24 | space, | |
| 25 | asker, | |
| 26 | askerVisited: visits.includes(asker.user_id), | |
| 27 | rule, | |
| 28 | people, | |
| 29 | visited: (p) => visits.includes(p.user_id), | |
| 30 | agent_mode: "edit", | |
| 31 | }); | |
| 32 | } | |
| 33 | ||
| 34 | test("audience rules: none or only the asker is the asker; more than 20 people is the workspace", () => { | |
| 35 | assert.deepEqual(audienceRule(null, "ana"), { kind: "asker" }); | |
| 36 | assert.deepEqual(audienceRule({ kind: "people", user_ids: ["ana"] }, "ana"), { kind: "asker" }); | |
| 37 | assert.deepEqual(audienceRule({ kind: "people", user_ids: ["ana", "bo", "bo"] }, "ana"), { kind: "people", user_ids: ["bo"] }); | |
| 38 | assert.deepEqual(audienceRule({ kind: "workspace" }, "ana"), { kind: "workspace" }); | |
| 39 | const crowd = Array.from({ length: 21 }, (_, i) => `u${i}`); | |
| 40 | assert.deepEqual(audienceRule({ kind: "people", user_ids: crowd }, "ana"), { kind: "workspace" }); | |
| 41 | const twenty = Array.from({ length: 19 }, (_, i) => `u${i}`); | |
| 42 | assert.equal(audienceRule({ kind: "people", user_ids: twenty }, "ana").kind, "people"); | |
| 43 | }); | |
| 44 | ||
| 45 | test("rule 1: a public channel finds only open-space and workspace-wide folios", () => { | |
| 46 | const ws: AudienceRule = { kind: "workspace" }; | |
| 47 | assert.equal(agentMayFind(reach(node("a", { space_id: "open" }), {}, open, ws)), true); | |
| 48 | assert.equal(agentMayFind(reach(node("a", { general_access: "workspace", general_role: "view" }), {}, null, ws)), true); | |
| 49 | // Private, shared, team, link: never in a public channel. | |
| 50 | assert.equal(agentMayFind(reach(node("a"), {}, null, ws)), false); | |
| 51 | assert.equal(agentMayFind(reach(node("a"), { a: [{ principal: "user:bo", role: "view" }] }, null, ws)), false); | |
| 52 | assert.equal(agentMayFind(reach(node("a", { space_id: "team" }), {}, team, ws)), false); | |
| 53 | assert.equal(agentMayFind(reach(node("a", { general_access: "link", general_role: "view" }), {}, null, ws, [], ["ana"])), false); | |
| 54 | }); | |
| 55 | ||
| 56 | test("rule 1: a conversation finds only what everyone in it can read", () => { | |
| 57 | const withBo: AudienceRule = { kind: "people", user_ids: ["bo"] }; | |
| 58 | const shared = node("a"); | |
| 59 | assert.equal(agentMayFind(reach(shared, { a: [{ principal: "user:bo", role: "view" }] }, null, withBo, [bo])), true); | |
| 60 | assert.equal(agentMayFind(reach(shared, {}, null, withBo, [bo])), false); | |
| 61 | // A team space: Cy is in the team, Bo isn't. | |
| 62 | const t = node("t", { space_id: "team" }); | |
| 63 | assert.equal(agentMayFind(reach(t, {}, team, { kind: "people", user_ids: ["cy"] }, [cy])), true); | |
| 64 | assert.equal(agentMayFind(reach(t, {}, team, withBo, [bo])), false); | |
| 65 | // A link folio: only when everyone opened it. | |
| 66 | const link = node("l", { general_access: "link", general_role: "view" }); | |
| 67 | assert.equal(agentMayFind(reach(link, {}, null, withBo, [bo], ["ana"])), false); | |
| 68 | assert.equal(agentMayFind(reach(link, {}, null, withBo, [bo], ["ana", "bo"])), true); | |
| 69 | }); | |
| 70 | ||
| 71 | test("rule 1: the asker's own Private is theirs alone", () => { | |
| 72 | assert.equal(agentMayFind(reach(node("p"), {}, null, { kind: "asker" })), true); | |
| 73 | assert.equal(agentMayFind(reach(node("p"), {}, null, { kind: "people", user_ids: ["bo"] }, [bo])), false); | |
| 74 | // Someone else's Private: not even for its asker. | |
| 75 | assert.equal(agentMayFind(reach(node("p", { owner: "user:bo" }), {}, null, { kind: "asker" })), false); | |
| 76 | }); | |
| 77 | ||
| 78 | test("rule 2: reading what the audience can't all read says so", () => { | |
| 79 | const r = reach(node("p"), {}, null, { kind: "workspace" }); | |
| 80 | assert.equal(r.asker_role, "manage"); | |
| 81 | assert.equal(r.audience_can_read, false); | |
| 82 | const fine = reach(node("o", { space_id: "open" }), {}, open, { kind: "workspace" }); | |
| 83 | assert.equal(fine.audience_can_read, true); | |
| 84 | }); | |
| 85 | ||
| 86 | test("an agent's grant never widens what it can do for its asker", () => { | |
| 87 | const n = node("f", { owner: "user:cy" }); | |
| 88 | const grants = { f: [{ principal: "agent:ag1", role: "manage" as const }, { principal: "user:bo", role: "comment" as const }] }; | |
| 89 | const r = reach(n, grants, null, { kind: "asker" }, [], [], bo); | |
| 90 | assert.equal(r.asker_role, "comment"); | |
| 91 | assert.deepEqual(r.can, { read: true, suggest: true, edit: false }); | |
| 92 | const none = reach(n, grants, null, { kind: "asker" }, [], [], ana); | |
| 93 | assert.equal(none.asker_role, null); | |
| 94 | assert.deepEqual(none.can, { read: false, suggest: false, edit: false }); | |
| 95 | assert.equal(agentMayFind(none), false); | |
| 96 | }); |