policy.event_tenant_unproved
An event or a batch names a tenant its action never reached under its caller's policy.
Area 9, Policy; not declinable; the next step is usually the agent's.
An event's subscribers run as the system within the tenant its field of
the tenant's id type names, and a batch's run within the one its saved
values of that type name (area 12). That id must come from a record of
the tenant the action read or wrote under its caller's policy, or a caller
could aim the system at a tenant they don't belong to. Here the event names the household the caller passed, not
the chore's:
type Handle = Text { max_length: 40}type Title = Text { max_length: 80}type HomeRole = Enum { member}entity Housemate { id: Uuid handle: Handle { unique: true } policy: { read: [id] tenant: Tenancy.shared }}principal { user: { subject: Housemate.handle }}entity Household { id: Uuid name: Title policy: { read: [HomeRole.member] }}entity HomeMembership { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } person_id: Housemate.id { on_delete: OnDelete.cascade } role: HomeRole membership: { scope: household_id member: person_id role: role } policy: { read: [person_id] }}entity Chore { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } title: Title thanked: Bool policy: { read: [HomeRole.member] update: [HomeRole.member] }}policy { tenant: Household}type ChoreThanked = Event { chore_id: Chore.id household_id: Household.id}action thank(chore_id: Chore.id, household_id: Household.id) fails ChoreNotFound -> Unit { var chore: Chore = attempt Chore.update(chore_id) { set: { thanked: true } } emit ChoreThanked { chore_id: chore.id household_id: household_id }}
error thank sends ChoreThanked to a Household it never reached
--> main.jadpo:92:23
|92 | household_id: household_id | ^^^^^^^^^^^^ `household_id`, whatever the caller passes
- reason
ChoreThanked's subscribers run as the system within theHouseholditshousehold_idnames, so that id must come from aHousehold, or a record of one, thatthankread or wrote under its caller's policy; one the caller passes could name aHouseholdthey don't belong to.- next
- Name the
Householdof a recordthankreads or writes, or read it first withattempt Household.get(household_id)(agent) - rule
- policy.
event_ tenant_ unproved - help
- https://jadpo.
dev/docs/errors/policy. event_ tenant_ unproved
The chore was changed under the caller's grants, so its household is one they belong to; a caller who isn't a member finds no chore, and the action fails:
type Handle = Text { max_length: 40}type Title = Text { max_length: 80}type HomeRole = Enum { member}entity Housemate { id: Uuid handle: Handle { unique: true } policy: { read: [id] tenant: Tenancy.shared }}principal { user: { subject: Housemate.handle }}entity Household { id: Uuid name: Title policy: { read: [HomeRole.member] }}entity HomeMembership { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } person_id: Housemate.id { on_delete: OnDelete.cascade } role: HomeRole membership: { scope: household_id member: person_id role: role } policy: { read: [person_id] }}entity Chore { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } title: Title thanked: Bool policy: { read: [HomeRole.member] update: [HomeRole.member] }}policy { tenant: Household}type ChoreThanked = Event { chore_id: Chore.id household_id: Household.id}action thank(chore_id: Chore.id, household_id: Household.id) fails ChoreNotFound -> Unit { var chore: Chore = attempt Chore.update(chore_id) { set: { thanked: true } } emit ChoreThanked { chore_id: chore.id household_id: chore.household_id }}
A batch's key: naming the household the caller passed is refused the
same way:
type Handle = Text { max_length: 40}type Title = Text { max_length: 80}type HomeRole = Enum { member}entity Housemate { id: Uuid handle: Handle { unique: true } policy: { read: [id] tenant: Tenancy.shared }}principal { user: { subject: Housemate.handle }}entity Household { id: Uuid name: Title policy: { read: [HomeRole.member] }}entity HomeMembership { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } person_id: Housemate.id { on_delete: OnDelete.cascade } role: HomeRole membership: { scope: household_id member: person_id role: role } policy: { read: [person_id] }}entity Chore { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } title: Title times_done: Int policy: { read: [HomeRole.member, Access.system] update: [Access.system] }}policy { tenant: Household}type ChorePage = Input { page_size: PageSize { default: 25 } after: PageCursor { optional: true }}query all_chores(page: ChorePage) -> Page<Chore> { from: Chore order: [Chore.id asc] page: { size: page.page_size after: page.after }}action bump(chore: Chore) fails ChoreNotFound -> Unit { attempt Chore.update(chore.id) { set: { times_done: Chore.times_done + 1 } }}action sweep_house(household_id: Household.id, page: ChorePage) -> BatchRun { return batches.each(from: all_chores(page), each: chore -> bump(chore), key: household_id)}
error sweep_house starts a batch in a Household it never reached
--> main.jadpo:106:82
|106 | return batches.each(from: all_chores(page), each: chore -> bump(chore), key: household_id) | ^^^^^^^^^^^^ `household_id`, whatever the caller passes
- reason
- A batch's run works as the system within the
Householdits saved values of typeHousehold.idname, so each must come from aHousehold, or a record of one, thatsweep_houseread or wrote under its caller's policy; one the caller passes could name aHouseholdthey don't belong to. - next
- Name the
Householdof a recordsweep_housereads or writes, or read it first withattempt Household.get(household_id)(agent) - rule
- policy.
event_ tenant_ unproved - help
- https://jadpo.
dev/docs/errors/policy. event_ tenant_ unproved
Reading the household first, through the caller's membership, proves it:
type Handle = Text { max_length: 40}type Title = Text { max_length: 80}type HomeRole = Enum { member}entity Housemate { id: Uuid handle: Handle { unique: true } policy: { read: [id] tenant: Tenancy.shared }}principal { user: { subject: Housemate.handle }}entity Household { id: Uuid name: Title policy: { read: [HomeRole.member] }}entity HomeMembership { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } person_id: Housemate.id { on_delete: OnDelete.cascade } role: HomeRole membership: { scope: household_id member: person_id role: role } policy: { read: [person_id] }}entity Chore { id: Uuid household_id: Household.id { on_delete: OnDelete.cascade } title: Title times_done: Int policy: { read: [HomeRole.member, Access.system] update: [Access.system] }}policy { tenant: Household}type ChorePage = Input { page_size: PageSize { default: 25 } after: PageCursor { optional: true }}query all_chores(page: ChorePage) -> Page<Chore> { from: Chore order: [Chore.id asc] page: { size: page.page_size after: page.after }}action bump(chore: Chore) fails ChoreNotFound -> Unit { attempt Chore.update(chore.id) { set: { times_done: Chore.times_done + 1 } }}action sweep_house( household_id: Household.id, page: ChorePage) fails HouseholdNotFound -> BatchRun { var household: Household = attempt Household.get(household_id) return batches.each(from: all_chores(page), each: chore -> bump(chore), key: household.id)}
Markdown for agents: /docs/errors/policy.event_tenant_unproved.md.Built from Jadpo main at aee2e8f.