Summary#
app Library {
use Osysharp.Privacy;
use Osysharp.Agents; // …and Osysharp.Privacy.Agents and Osysharp.Privacy.Llm arrive by themselves
}
var outcome = await Agents.Of<Desk>().Do(new DraftReminder(), loan);
MarkAgentWorkAbout(outcome.Task, new DataSubject { Emails = [loan.BorrowerEmail] }); // whose data that work isSignature#
// Osysharp.Privacy.Agents — completes Osysharp.Privacy + Osysharp.Agents
void MarkAgentWorkAbout(Guid task, DataSubject subject) // work ABOUT somebody: by account, address or number
List<AgentTask> AgentWorkOf(DataSubject subject) // the top piece of each piece of work for or about them
class AgentsPersonalData : IPersonalDataHolder // discovered — categories agents.trace, agents.record
app.AgentsPrivacy = new AgentsPrivacySetup { KeepContentMonths = 1, KeepRecordMonths = 7 };
// Osysharp.Privacy.Llm — completes Osysharp.Privacy + Osysharp.Llm
class ModelCallsPersonalData : IPersonalDataHolder // discovered — category llm.calls
app.ModelCallsPrivacy = new ModelCallsPrivacySetup { KeepMonths = 13 };
// Osysharp.Llm — what the meter's holder calls
int ForgetModelCallRequester(Guid requester, DateTime? before = null) // needs ManagesModelSpend
bool ModelCallsInFlightFor(Guid requester)
// Osysharp.Privacy.Assignments — completes Osysharp.Privacy + Osysharp.Assignments
void MarkAssignmentAbout(Assignment a, DataSubject subject) // and everything opened under it, and what its work stands for
List<Assignment> AssignmentsAbout(DataSubject subject)
class AssignmentsPersonalData : IPersonalDataHolder // discovered — categories assignments.about, assignments.people
class AssignmentPrivacyRules { virtual List<string> PersonalWords(Assignment a) } // the words that name them
app.AssignmentsPrivacy = new AssignmentsPrivacySetup { KeepAboutMonths = 24, KeepPeopleMonths = 0, StandIn = "a former customer",
FormerColleague = "a former colleague", AlwaysBlank = false, Rules = … };
// Osysharp.Privacy.WorkRepositories — completes Osysharp.Privacy + Osysharp.WorkRepositories
void MarkItemAbout(RepositoryItem item, DataSubject subject)
List<RepositoryItem> ItemsAbout(DataSubject subject, DataCategory category)
class WorkRepositoriesPersonalData : IPersonalDataHolder // discovered — repositories.<kind>, .items, .contributions
app.WorkRepositoriesPrivacy = new WorkRepositoriesPrivacySetup { KeepOtherMonths = 0 };Description#
What do I have to write?#
Nothing for what is done FOR or BY a signed-in person, and one line for what is ABOUT somebody. Every holder is
discovered, and each package completes the two kits it sits between, so osy lock
includes it in any app that uses Osysharp.Privacy with Osysharp.Agents, Osysharp.Llm, Osysharp.Assignments
or Osysharp.WorkRepositories.
Say it once, where the work starts#
A mark on an assignment covers what hangs under it: the assignments opened under it, the row each piece of attached
work stands for — an agent's receipt — and every item in its repository; and the agent work a repository item was
written in. So a case opened about a customer needs one line —
MarkAssignmentAbout(a, new DataSubject { Emails = [order.CustomerEmail] }) — and one request then reaches the case,
the agent's record of the work it did on it, and what that work found, each through its own holder. Each holder says
which rows hang under which (ISubjectLinks, The Privacy kit — erasure that keeps only what the company must or may); nothing is wired between them.
Whose data is a piece of agent work?#
- Done for a person — they asked, or somebody asked on their behalf (
For(person)). The receipt names them (AgentTask.Requester,StartedBy), and a request finds it by the account it proved. - About a person — a reminder drafted about a borrower, a claim checked for a customer. Only the app knows, so
it says:
MarkAgentWorkAbout(outcome.Task, subject), with an account, an address or a number. The note keeps a keyed fingerprint of an address, never the address; anybody doing the work may write one and only the privacy desk reads it back. It isOsysharp.Privacy'sMarkAbout(The Privacy kit — erasure that keeps only what the company must or may) with the kind filled in.
A piece of work is found whole: the top piece and everything it handed on share one record and one chain. Work nobody marked is still found as the work of whoever gave it, but not by the address of the person it was about.
What is held, and for how long?#
| category | what | kept by default | |
|---|---|---|---|
agents.trace | what the work said: every line's content, what was asked and answered when it asked for help, the result | 1 month after it ended | |
agents.record | that it happened: agent, skill, who it was for, what it was about, each step's sentence, model, tool and cost, how it ended, the chain | 7 months after it ended | |
llm.calls | the model calls made for a person: when, which model, tokens, cost — the meter keeps no text | 13 months on their name | |
agents.corrections | what somebody wrote to correct work done for or about a person, and the instructions an agent is given for that person's work (InstructionDraft.For, or marked with MarkAgentInstructionAbout) | 7 months after it was written, and never longer than the record of the work it corrects | |
agents.teaching | that a person taught: the corrections they wrote and the instructions they proposed, activated, retired or rolled back — by their account | while the agent is told it | |
agents.oversight | that a person oversaw the agents: the [[agent-autonomy#standing | standing rules]] they gave or took back, the times they paused, resumed or sponsored an agent or were named its sponsor, the questions an agent put to them and their answers, the proposals sent to them or decided by them — by their account | while what was decided is on record |
agents.handovers | that a person handed an inbox item to an agent — with Osysharp.Agents.HumanRequests, through Osysharp.Privacy.Agents.HumanRequests | while the item is kept |
An erasure of agents.corrections takes each correction's words and erases an instruction for the person — retired
first, which is the agent's next instruction set, made by nobody; that the work was corrected stays as a count. A
restriction withholds such an instruction, so the agent stops being sent it. A standing rule for the person's work
("from now on, alone") is in agents.corrections too: erased, it is taken back and no longer says whose work it was
(AgentStandingRule.ErasedAt) — so it never comes to cover everybody's; kept out of use, nobody reads it, and the
agent proposes again. An erasure of agents.teaching takes the person off what they taught; what was said and decided
stays, and the agent goes on being told it. An erasure of agents.oversight or agents.handovers takes the person off
in the same way (ForgetWhoOversawAgents, ForgetWhoHandedWorkToAgents); it waits while a question or a proposal
still waits on them, while they sponsor an agent, or while an agent still holds an item they handed over.
All of them are held on legitimate interest, so an erasure erases them unless somebody records compelling grounds.
The defaults are the app's (AgentsPrivacySetup, ModelCallsPrivacySetup) and the tenant's on the retention page.
Seven months is six and a margin: where the EU AI Act applies, Art. 19 and Art. 26(6) ask for a high-risk system's
logs to be kept at least six months. Whether it applies is the tenant's to establish.
What about assignments?#
Two different people are in one. The people ON it — who asked, who owns it, who did the work — are found by their
account (assignments.people); an erasure takes them off: their seat goes, nothing on it is theirs, and what they
did reads as done by Somebody no longer here. The person it is ABOUT is found by the mark
(assignments.about); an erasure replaces the words that say who it was about by the app's stand-in — "a former
customer" — in every sentence, and leaves what was done and how it ended. A request proves an address, not a name,
so the app's AssignmentPrivacyRules.PersonalWords(a) names the words; when it names none and no account is
known, every sentence is erased whole rather than a name left standing. An assignment about somebody is erased once
it is closed — until then the request waits — and loses those words for everybody 24 months after it closed.
What about a repository's items?#
Each kind the app keeps for a time of its own (KindRule.RetainFor, Work repositories — what an assignment knows, kept as items (WorkRepositories kit)) is a category of
its own — repositories.document, repositories.finding — with that period, which the tenant reads and changes on
the retention page; every other kind is repositories.items. An item about a person is erased when they ask and
when its kind's period ends: what it said goes from every version, with its notes and its title on the assignment's
activity; that there was one, what kind, who added it and when, stay. Who reads which item is the same before and
after. A contributor who leaves (repositories.contributions, by account) is taken off what they added, which
stays.
What does an erasure leave of an agent's work?#
What was said is emptied, for the auditor too. The person is taken off the work: it is nobody's, about nothing, and
each line's sentence reads Erased. What stays is that the work happened — the steps in order, each one's kind,
model, tool and cost, and how it ended — and the proof that none of it was changed.
That proof survives because the agents kit chains a record through the hashes of its words, not the words
(The receipt and the record — what every piece of an agent's work leaves (Agents kit)). After an erasure VerifyTrace(task) answers Intact, with Erased saying how many lines no
longer say what they said. A cost, a tool or a step changed afterwards still breaks its line's hash, and a sentence
written into an erased line is refused by name.
On the meter, an erasure takes the person off: each row loses who it was for and the requester: line of what it
was charged to; what was counted against a budget of their own is deleted. The rows, and what they cost, stay
counted for the app.
Who erases?#
A request's own run, as the app itself. The agents kit's erasure functions ask the app's ErasesAgentRecords policy,
which is nobody's until the app says otherwise — and it need not say. A person who manages privacy and audits
agents is still refused when they call one by hand.
What about work still running, a hold, a restriction?#
- Still running — the request waits ("an AI agent is still working on something for you or about you") and is
asked again by the daily purge. A model call still on its way holds
llm.callsthe same way. - A legal hold (Legal holds — nothing a dispute needs is erased or purged while it lasts) — a held person's work, and work marked with a held address, is neither erased nor purged while it stands.
- A restriction — their work, and their rows of the meter, leave every ordinary read until it is lifted.
- Every day, for everybody — content past its period is emptied, work past the record's period is deleted whole, and a call past its period loses who it was for.
Examples#
app Library {
use Osysharp.Identity;
use Osysharp.WorkQueues;
use Osysharp.Mail;
use Osysharp.Privacy;
use Osysharp.Agents;
use Osysharp.Agents.Testing;
use Osysharp.Llm;
// Both arrive by themselves under `osy lock`; this example is one file, so it names them.
use Osysharp.Privacy.Agents;
use Osysharp.Privacy.Llm;
use Osysharp.Workflow;
use Osysharp.Observability;
use Osysharp.Storage;
use Osysharp.Ui;
model "model/**/*.osy";
}
using Osysharp.Agents;
using Osysharp.Agents.Testing;
using Osysharp.Identity;
using Osysharp.Llm;
using Osysharp.Privacy;
using Osysharp.Privacy.Agents;
app.Llm = new LlmSetup {
Models = [ new LlmModel { Name = "Everyday", Client = new ScriptedModel { Name = "Everyday" }, Model = "scripted-everyday",
Price = new LlmPrice { Input = 1, Output = 5 }, MaxOutputTokens = 1000 } ],
};
app.Agents = new AgentsSetup { Agents = [new Desk()], TaskBudget = 0.05m };
[Role] enum LibraryRole { Authenticator, Admin, Librarian, Member, Desk }
partial entity RoleGrant { security { allow read when IsAuthenticated; } }
policy IsAdmin => RoleGrant.Any(g => g.Member == user && g.Role == LibraryRole.Admin);
policy IsLibrarian => RoleGrant.Any(g => g.Member == user && g.Role == LibraryRole.Librarian);
policy IsMember => RoleGrant.Any(g => g.Member == user && g.Role == LibraryRole.Member);
policy IsDesk => RoleGrant.Any(g => g.Member == user && g.Role == LibraryRole.Desk);
// Privacy's questions, and the Inbox's (a request is inbox work).
policy ManagesPrivacy => IsAdmin;
policy PlacesLegalHolds => IsAdmin;
policy WorksInbox => IsAdmin;
policy LeadsInbox => IsAdmin;
policy AdministersInbox => IsAdmin;
policy ManagesOrganizations => IsAdmin;
policy CollaboratesOnRecords => IsAdmin;
// The agents' questions. Nobody is named to erase their records: a privacy request's own run does.
policy UsesAgents => IsMember || IsLibrarian;
policy SupervisesAgents => IsLibrarian;
policy AuditsAgents => IsAdmin;
policy ReadsModelUsage => IsAdmin;
policy ManagesModelSpend => IsAdmin;
entity Person : Principal {
security { allow read when IsAuthenticated || IsAuthenticator; allow create when IsAuthenticator; }
}
/// One book out on loan.
entity Loan {
[Required, MaxLength(200)] string Title;
Person? Borrower;
[MaxLength(320)] string BorrowerEmail = "";
int Renewals = 0;
security {
allow read, update when IsMember where Borrower == user;
allow read, update, create when IsLibrarian || IsDesk;
}
}
/// Give a loan three more weeks.
[Action("Renew a loan")]
public string RenewLoan(Loan loan) { loan.Renewals = loan.Renewals + 1; return $"“{loan.Title}” is renewed."; }
/// What the desk hands back.
public class Renewed { public int Loans; public string Said = ""; }
/// Renews what a member asks to have renewed.
public class RenewLoans : Skill<string, Renewed> {
public override string Description => "Renew the loans a member asks about.";
public override List<ActionInfo> Tools => [Actions.Of(RenewLoan)];
}
/// The library's desk.
[Actor(Roles = [LibraryRole.Desk])]
public class Desk : Agent {
public static Person Principal => new Person { Email = "[email protected]", DisplayName = "The desk" };
public override string Purpose => "You are the library's desk: you renew loans.";
public override List<SkillBase> Skills => [new RenewLoans()];
public override Permits May => Permits.ChangesAlone(); // it renews a loan by itself
}
/// A member asks the desk to renew a loan — work done FOR whoever is signed in.
public AgentOutcome<Renewed> AskTheDesk(string wish) {
return await Agents.Of<Desk>().Do(new RenewLoans(), wish, about: "Renewals for " + Session.CurrentUser!.DisplayName);
}
[SuppressWarning("IMPORTED_ENTITY_HAS_NO_POLICY")] partial entity WorkItem { }
[TestFixture]
void Town() {
var ada = new Person { Email = "[email protected]", DisplayName = "Ada" };
var sol = new Person { Email = "[email protected]", DisplayName = "Sol" };
var mia = new Person { Email = "[email protected]", DisplayName = "Mia Berg" };
new RoleGrant { Member = ada, Role = LibraryRole.Admin };
new RoleGrant { Member = sol, Role = LibraryRole.Librarian };
new RoleGrant { Member = mia, Role = LibraryRole.Member };
new Loan { Title = "The Dispossessed", Borrower = mia, BorrowerEmail = "[email protected]" };
new AgentSeat { Agent = "Desk", Sponsor = sol }; // the librarian answers for the desk
}
principal Ada => Person.Single(p => p.Email == "[email protected]");
principal Mia => Person.Single(p => p.Email == "[email protected]");
[Test(Town)]
void The_chain_verifies_after_a_member_is_erased() {
Guid task = Guid.Empty;
Guid mia = Guid.Empty;
runas (Mia) {
mia = Session.CurrentUser!.Id;
var loan = Loan.Single(l => l.Title == "The Dispossessed");
var script = AgentScript.Of(
Says.Tool(Actions.Of(RenewLoan), "{\"loan\":\"" + loan.Id.ToString() + "\"}"),
Says.HandBack(new Renewed { Loans = 1, Said = "Mia, The Dispossessed is yours for three more weeks." }));
ScriptedAnswer.Stub((model, request) => script.Answer(model, request));
task = AskTheDesk("Please renew The Dispossessed. Mia Berg, card 4471.").Task;
}
runas (Ada) {
// Her copy would carry every word of it; a plan counts one piece of work in each category.
var holder = new AgentsPersonalData();
var subject = new DataSubject { Account = mia };
Assert.Contains("card 4471", holder.Export(subject, CategoryOf("agents.trace")).Json);
Assert.Equal(1, holder.Count(subject, CategoryOf("agents.record")));
// She asks to be erased; Ada checked it is her, and approves the plan. The request's own run erases.
var r = FileRequest(RequestKind.Erasure, mia, "", IdentityCheck.InPerson, "She asked at the desk with her library card.");
ApproveRequest(r);
Assert.Equal(RequestState.Done, PrivacyRequest.Single(x => x.Id == r.Id).State);
}
runas (Ada) {
var receipt = AgentTask.Single(t => t.Id == task);
// Nobody's, about nothing, and not a word of it left — for the auditor, who reads everything there is.
Assert.Null(receipt.Requester);
Assert.Equal("", receipt.About);
Assert.Null(receipt.Result);
Assert.Equal(0, TraceEvent.Count(e => e.Task.Id == task && e.Payload != null && e.Payload != ""));
Assert.Equal(6, TraceEvent.Count(e => e.Task.Id == task && e.Summary == "Erased."));
// What happened is still told, and the chain still verifies.
Assert.Equal(["RenewLoan"], Trajectory.Of(task));
Assert.Equal(400, receipt.SpentMicros);
var check = VerifyTrace(receipt);
Assert.True(check.Intact);
Assert.Equal(6, check.Lines);
Assert.Equal(6, check.Erased);
// A line changed after the erasure is still found.
var lines = TraceOf(receipt);
lines[1].CostMicros = 1;
Assert.Equal("Line 2 is not as it was written.", CheckChain(lines).Problem);
lines = TraceOf(receipt);
lines[0].Summary = "The desk was given nothing.";
Assert.Equal("Line 1 was erased, and something was written in its place.", CheckChain(lines).Problem);
// The model calls are still on the meter, on nobody's name.
Assert.Equal(0, ModelCall.Count(m => m.Requester == mia));
Assert.Equal(2, ModelCall.Count());
// Nobody erased it by hand: Ada manages privacy and audits the desk, and is refused.
Assert.Denied(() => ForgetAgentWork(receipt));
}
runas (Mia) { Assert.Equal(0, AgentTask.Count()); }
}app Studio {
use Osysharp.Identity;
use Osysharp.WorkQueues;
use Osysharp.Mail;
use Osysharp.Privacy;
use Osysharp.Assignments;
use Osysharp.WorkRepositories;
use Osysharp.Llm;
// Both arrive by themselves under `osy lock`; this example is one file, so it names them.
use Osysharp.Privacy.Assignments;
use Osysharp.Privacy.WorkRepositories;
use Osysharp.Workflow;
use Osysharp.Observability;
use Osysharp.Storage;
use Osysharp.Ui;
model "model/**/*.osy";
}
using Osysharp.Assignments;
using Osysharp.Identity;
using Osysharp.Privacy;
using Osysharp.Privacy.Assignments;
using Osysharp.Privacy.WorkRepositories;
using Osysharp.WorkRepositories;
// Applications are kept six months after the hire closes; what an interviewer thought is read by fewer people.
app.WorkRepositories = new WorkRepositoriesSetup {
Kinds = [ new KindRule { Kind = ItemKind.Document, RetainFor = TimeSpan.FromDays(180) },
new KindRule { Kind = ItemKind.Finding, Restricted = true } ],
};
// A case about a candidate reads, once she is erased, as a case about "a former candidate".
app.AssignmentsPrivacy = new AssignmentsPrivacySetup { StandIn = "a former candidate", Rules = new StudioPrivacy() };
[Role] enum StudioRole { Authenticator, Admin, Manager, Interviewer }
partial entity RoleGrant { security { allow read when IsAuthenticated; } }
policy IsAdmin => RoleGrant.Any(g => g.Member == user && g.Role == StudioRole.Admin);
policy IsManager => RoleGrant.Any(g => g.Member == user && g.Role == StudioRole.Manager);
// Privacy's questions, and the Inbox's (a request is inbox work).
policy ManagesPrivacy => IsAdmin;
policy PlacesLegalHolds => IsAdmin;
policy WorksInbox => IsAdmin;
policy LeadsInbox => IsAdmin;
policy AdministersInbox => IsAdmin;
policy ManagesOrganizations => IsAdmin;
policy CollaboratesOnRecords => IsAdmin;
// The manager asks for the hire and oversees it. Nobody is named to erase: a privacy request's own run does.
policy OpensAssignments => IsManager;
policy OverseesAssignments => IsManager;
entity Person : Principal {
security { allow read when IsAuthenticated || IsAuthenticator; allow create when IsAuthenticator; }
}
/// The studio's own note of who a reference check is about.
entity Candidate {
[Required, MaxLength(200)] string Name;
[MaxLength(320)] string Email = "";
Assignment? Check;
security { allow read, create, update when IsManager || IsAdmin; }
}
/// The words in a case's text that say who it is about: the candidate's name, as the studio's note has it.
public class StudioPrivacy : AssignmentPrivacyRules {
public override List<string> PersonalWords(Assignment a) {
var c = Candidate.FirstOrDefault(x => x.Check == a);
return c == null ? [] : [c!.Name, c!.Name.Split(" ")[0]];
}
}
/// An application arrives — and the studio says whose data it is.
public RepositoryItem ReceiveApplication(Assignment hire, string name, string email, string text) {
var item = AddItem(hire, new NewItem { Kind = ItemKind.Document, Title = "Application: " + name, Content = text });
MarkItemAbout(item, new DataSubject { Emails = [email] });
return item;
}
[SuppressWarning("IMPORTED_ENTITY_HAS_NO_POLICY")] partial entity WorkItem { }
[TestFixture]
void Office() {
var ada = new Person { Email = "[email protected]", DisplayName = "Ada" };
var hanna = new Person { Email = "[email protected]", DisplayName = "Hanna" };
new RoleGrant { Member = ada, Role = StudioRole.Admin };
new RoleGrant { Member = hanna, Role = StudioRole.Manager };
new Candidate { Name = "Maja Okafor", Email = "[email protected]" };
}
principal Ada => Person.Single(p => p.Email == "[email protected]");
principal Hanna => Person.Single(p => p.Email == "[email protected]");
Assignment Job(Guid id) { return Assignment.Single(a => a.Id == id); }
RepositoryItem Item(Guid id) { return RepositoryItem.Single(i => i.Id == id); }
[Test(Office)]
void A_candidate_who_withdraws_is_erased_and_the_other_application_goes_when_its_time_is_up() {
TestClock.Set(new DateTime(2026, 3, 2, 10, 0, 0));
Guid hire = Guid.Empty;
Guid majas = Guid.Empty;
Guid svens = Guid.Empty;
runas (Hanna) {
var a = OpenAssignment(new NewAssignment { Title = "Hire an illustrator", Goal = "An illustrator has accepted.", Criteria = ["The decision is recorded"] });
hire = a.Id;
majas = ReceiveApplication(a, "Maja Okafor", "[email protected]", "Ten years of picture books.").Id;
svens = ReceiveApplication(a, "Sven Lind", "[email protected]", "Editorial work for two dailies.").Id;
}
// Maja withdraws and asks to be erased. The hire is open, and goes on.
runas (Ada) {
var r = FileRequest(RequestKind.Erasure, null, "[email protected]", IdentityCheck.KnownDetails, "She wrote from the address on her application.");
ApproveRequest(r);
Assert.Equal(RequestState.Done, PrivacyRequest.Single(x => x.Id == r.Id).State);
}
runas (Hanna) {
// What her application said is gone; its place in the record is not.
Assert.Equal("", CurrentOf(Item(majas)).Content);
Assert.Equal("Document (erased)", Item(majas).Title);
Assert.NotNull(Item(majas).ErasedAt);
Assert.Equal(Hanna.Id, Item(majas).AddedBy!.Id);
// The hire's activity no longer names her, and Sven's application is word for word as it was.
Assert.False(Job(hire).Activity.Any(l => l.Said.Contains("Maja")));
Assert.Equal("Editorial work for two dailies.", CurrentOf(Item(svens)).Content);
Assert.Equal(AssignmentState.Open, Job(hire).State);
MeetCriterion(Job(hire).Criteria.Single(), "We offer Sven the place.");
CompleteAssignment(Job(hire), "Sven accepted.");
}
// Nobody asks. Six months and a few days after the hire closed, Sven's application reaches its time.
TestClock.Set(new DateTime(2026, 9, 7, 10, 0, 0));
runas (Ada) { PurgeNow(); }
runas (Hanna) {
Assert.Equal("", CurrentOf(Item(svens)).Content);
Assert.Equal("It was kept as long as its kind is.", Item(svens).ErasedWhy);
}
}
[Test(Office)]
void A_case_about_a_candidate_reads_as_about_a_former_candidate() {
Guid check = Guid.Empty;
runas (Hanna) {
var a = OpenAssignment(new NewAssignment {
Title = "Check Maja Okafor's reference", Goal = "We know what Maja's last studio thought.", Criteria = ["The reference is on file"] });
check = a.Id;
Candidate.Single(c => c.Name == "Maja Okafor").Check = a;
// Said once, for the case: everything in it is about her.
MarkAssignmentAbout(a, new DataSubject { Emails = ["[email protected]"] });
AddItem(a, new NewItem { Kind = ItemKind.Evidence, Title = "Call with the last studio", Content = "They would hire Maja again." });
}
runas (Hanna) {
MeetCriterion(Job(check).Criteria.Single(), "Maja's reference is on file.");
CompleteAssignment(Job(check), "A good reference for Maja Okafor.");
}
runas (Ada) {
var r = FileRequest(RequestKind.Erasure, null, "[email protected]", IdentityCheck.KnownDetails, "She wrote from the address on her application.");
ApproveRequest(r);
Assert.Equal(RequestState.Done, PrivacyRequest.Single(x => x.Id == r.Id).State);
}
runas (Hanna) {
var a = Job(check);
// Words where she was — the case still reads as a piece of work that was done.
Assert.Equal("Check a former candidate's reference", a.Title);
Assert.Equal("A good reference for a former candidate.", a.Outcome);
Assert.Equal("a former candidate's reference is on file.", a.Criteria.Single().MetBy);
Assert.Equal(AssignmentState.Completed, a.State);
Assert.False(a.Activity.Any(l => l.Said.Contains("Maja")));
// The item in its repository was nobody's to mark: it went with the case.
Assert.Equal("", CurrentOf(RepositoryItem.Single(i => i.Assignment.Id == check)).Content);
}
}See also#
- The Privacy kit — erasure that keeps only what the company must or may — requests, plans, the holder contract,
MarkAbout, the daily purge - Assignments — a piece of work owned until its outcome is reached (Assignments kit) — assignments, and the erasure functions their holder calls
- Work repositories — what an assignment knows, kept as items (WorkRepositories kit) — a repository's items, their kinds and their periods
- The receipt and the record — what every piece of an agent's work leaves (Agents kit) — the receipt, the record and its chain; the erasure functions a holder calls
- Model spend — budgets, the meter and the price book (Llm kit) — the meter, and taking a person off it
- Legal holds — nothing a dispute needs is erased or purged while it lasts — what keeps a person's data through an erasure and a purge
- personal data (Classification(Personal.X)) — the
Personallevels the agent kits' fields are classified with