Skip to main content
Version: Target · roadmap

Process · simple — answer an HR question, per person

The case. The same HR reporting, but each person acts under their own identity: a manager reads their teams, HR reads everyone, and a manager sees the employees' initials, not their names.

What the example shows. The identity passed to the tool (on_behalf_of), two profiles, guards on reads, a condition that ties the data to the identity, an output filter per profile.

examples/cible/process-simple.yaml
# Process · simple — CIBLE. HR answers, now per person: a manager reads their own teams only,
# HR reads everyone; names are reduced for managers; reads have guards too.
station:
id: rh-reporting
kind: process
name: Reporting RH
guidance: {max_mode: observe}
identity: # the identity providers this station accepts (ADR-031)
providers:
entra: {mechanism: oidc, verifies: email, issuer: "https://login.microsoftonline.com/acme/v2.0",
client_id_env: ENTRA_CLIENT_ID,
attributes: {roles: groups, teams: teams, manager: manager}} # what rules may read: identity.roles, .teams, .manager

global: # applies to every field of its type, every skill
language: fr
currency: EUR # every `type: price` field
locale: fr-FR
timezone: Europe/Paris

apis:
- name: rh
openapi_url: https://rh-api.example.fr/openapi.json
base_url: https://rh-api.example.fr
auth: {type: oauth2, on_behalf_of: identity} # the tool sees WHO asks, not one shared key
operations:
- {id: listTeams, access: read}
- {id: getOvertimeReport, access: read}

profiles:
manager: {when: "'manager' in identity.roles", grants: [rh.listTeams, rh.getOvertimeReport]}
rh: {when: "'hr' in identity.roles", grants: [rh.listTeams, rh.getOvertimeReport]}

skills:
- name: equipes
invoke:
method: listTeams
present: {fields: {name: {from: name, type: text, role: title}}}
render: {component: cards}

- name: rapport-heures-sup
params:
team: {type: text, set_by: [llm, person]}
invoke:
method: getOvertimeReport
args: {team: "$params.team"}
guards: # a read has guards too: at invoke
- {name: ses-equipes, require: "'rh' in profiles or $params.team in identity.teams"} # a manager reads their own teams
present:
fields:
total: {from: total_overtime_hours, type: quantity, role: headline}
person: {from: employee_name, type: text, class: personal}
per_profile:
manager: {output_filters: {reduce: {person: initials}}}
render:
component: report
report: {headline: total, unit: h, series: weeks, point_label: week_starting,
point_value: overtime_hours, highlight: worst_week}

agent:
name: Reporting RH
persona: "un analyste RH qui parle chiffres, brièvement"
tone: professional
portals:
teams:
app: reporting-rh
identity: {auth: entra} # Entra ID: roles and teams come from the directory

What happens​

who asksCoreresult
a manager, about their teamgrant of manager; team in identity.teamsthe report, names reduced to initials
a manager, about another teamthe condition does not holdrefused: "you do not have access to this team"
someone from HR, about any teamgrant of rhthe full report
an employee with no roleno profile, so no grantrefused

Key points​

  • A read has guards, declared under invoke.guards.
  • The HR tool receives the person's identity: its own access policy applies on top of the station's.