Roles and skills — one definition drives access, the home page, the SOPs and the AI¶
Angel, 2026-09-12: "we set a user with the role of invoicing or sales… we can control what they see in the home page, what SOPs they see, and when we build the skill, the skill will tell the AI what they can and cannot do."
The idea in one line¶
A role is written down once, in one file, and everything else reads it: who gets into which application, which tiles appear on the home page, which SOPs are visible, what the ERP lets them change, and what an AI assistant is allowed to do on their behalf.
Today the same knowledge is scattered: a department list hard-coded in onboard, groups created by
hand in InvenTree, tiles typed into index.html, and SOPs visible to everyone. Four places to
forget. One file fixes that.
The one file¶
~/Notes/roles/roles.yml, in the vault so it is reviewed and versioned like everything else.
sales:
title: Sales
erp_group: Sales & Purchasing
access: [home, sop, erp] # Cloudflare Access applications
nas: false # personal Samba login
tiles: [erp, sop, website] # home page
sops: [000, 002, 004, 005, 007] # visible SOPs
ai:
may: [look_up_part, check_stock, draft_sales_order, draft_invoice, draft_quote_reply]
never: [delete_anything, change_part_data, change_prices, send_email]
confirm: always # a person presses the button, never the assistant
Four layers, and which one is the real guard¶
| Layer | Enforces | Trust it? |
|---|---|---|
| Cloudflare Access | who reaches the door at all | yes — outside our code |
| ERP groups and a per-role API token | what the data will actually accept | yes — this is the real guard |
| Home page and SOP visibility | what a person is shown | no — convenience, not security |
| The AI skill definition | what the assistant offers to do | no — a prompt is a suggestion |
The rule that follows: never let the assistant hold a token wider than the person's own role. If sales cannot delete a part in the ERP, their assistant must fail the same way, at the API, not because a prompt asked it nicely.
Phases¶
Phase 1 — the registry. Write roles.yml. Point onboard at it instead of its hard-coded
department list. Add onboard roles to print the table. Nothing user-visible changes; the value is
one source of truth. Half a day.
Phase 2 — the home page follows the role. The home page becomes a Cloudflare Pages Function instead of a static file. Access already passes the signed identity of the viewer; the function verifies that signature against the team's public keys (never trusts a bare header), looks up the role, and renders only that role's tiles. Everyone still lands on the same address. One day.
Phase 3 — SOPs follow the role. Each SOP declares roles: [engineering, production] in its
front matter. The build emits a manifest of page to roles, and a function on the SOP site enforces
it: a page outside your role returns a polite refusal, and the navigation hides it. Document Control
and anything marked roles: [all] stays visible to everyone. One day.
Phase 4 — the assistant, one narrow skill at a time. Each role gets its own ERP user and token,
scoped by its ERP group. The assistant is handed that token and a list of allowed actions from
roles.yml. It drafts; a person confirms; nothing sends or posts itself. First skill: "invoice
customer X for ten of part Y" for Sales, because the document already exists and the failure mode is
visible. A week for the first one, days for each after.
What this is not¶
Not a security boundary drawn in the interface. Hiding a tile stops a mistake, not a determined person. The boundary is the ERP group and the token behind it, which is why Phase 1 and Phase 4 are the phases that matter and Phases 2 and 3 are polish.
Departments are the unit (2026-09-12, Angel's call)¶
A person is not given a bespoke role. They are assigned one or more departments, and their permissions, tiles, SOPs and assistant skills are the union of those departments. Two people today, but the shape is built for ten.
| Department | Flag | ERP group | Owns |
|---|---|---|---|
| Administration | admin |
Owner | everything, plus onboarding and settings |
| Engineering | engineering |
Engineering | parts, BOMs, the KiCad library |
| Production | production |
Assembler | builds, stock movement, test results |
| Purchasing | purchasing |
Purchasing | suppliers, purchase orders, receiving |
| Sales | sales |
Sales | customers, quotes, sales orders, invoices out |
| Finance | finance |
Finance | payment status, the money reports |
Today: Angel is admin. His wife is sales,finance — invoices and orders from Sales, payment and
the numbers from Finance. Nobody is a special case; when a third person arrives they get a
department, not a negotiation.
Multiple departments are additive and never subtractive, so sales,finance can do everything either
one can and nothing more. That keeps the question "what can this person do?" answerable by reading
two short lists instead of one long exception.
One procedure, two audiences¶
Every department has SOPs, which tell a person how to do the job, and skills, which tell the assistant how to help with the same job. They are written as a pair and reference each other:
~/Notes/skills/sales/SKILL.md → governed by SOP-007 Sales Orders and Invoicing
~/Notes/skills/purchasing/SKILL.md → governed by SOP-004 Purchase Orders, SOP-005 Goods Receiving
A skill file states, for one department: what the assistant may read, what it may draft, what it may never touch, and which step a person must confirm. It cites its SOP rather than restating it — the same rule the SOPs follow with each other. If the procedure changes, the SOP and the skill change in the same commit, or the assistant is working to a document that no longer exists.
Open questions¶
- The accounting decision above. It blocks half of what Business Office is meant to do.
- Should Sales see cost prices, or only sale prices? Seeing margin is useful for quoting and awkward once a third person is in that department.
- When the assistant drafts an invoice, does she press send, or does it come to you for approval first while the habit is new?
Departments versus functions — the structure question, answered (2026-09-13)¶
Angel asked what is best practice for long-term growth, prompted by "is Quality a department?". Researched against RBAC practice and ISO 9001:2015. The answer is a small change now that avoids an expensive one later.
What the practice says¶
- NIST/ANSI RBAC (INCITS 359) defines a role as a job function, deliberately not a job title and not a box on the org chart. Mirroring the org chart is a documented anti-pattern: titles and reporting lines churn far faster than duties do, so every reorg becomes a permissions rewrite, and the moment two people with one title need slightly different access somebody mints a new role. That is role explosion, and it is what the IAM literature warns about most consistently.
- ISO 9001:2015 removed the mandatory management representative precisely to stop quality being isolated in one appointed post. Clause 5.3 still requires that someone have documented authority for the quality system and for reporting on it — but it requires clear assignment of authority, not organisational independence, and certainly not a department. IPC-A-610 and J-STD-001 are workmanship standards and say nothing about org structure at all.
- In practice, shops of 2–10 people treat quality as a hat, not a department. A dedicated QA function appears around 15–30 people, or immediately if a customer contract demands it. What matters at our size is that whoever wears the hat has documented authority to reject or hold nonconforming product even when that is inconvenient for the schedule.
- Segregation of duties: nobody should hold two of authorisation, custody, recording and reconciliation for the same transaction. At two people that is impossible, so the accepted substitution is detection instead of prevention — the owner reviews source documents, and the system flags exceptions for after-the-fact sign-off.
The decision¶
Split department from function. One file still, two concepts.
- A department stays what it is today: the org-facing grouping — home tiles, who you ask, which board your tasks land on.
- A function becomes the addressable unit of permission:
po_create,po_approve,price_change,inventory_adjust,quality_hold,sop_owner,invoice_send. A department is then a bundle of functions, exactly as it behaves now, and a person can be granted a function directly without inventing a department to carry it.
Right now, at two people, department and function are nearly the same set, which is why the union model has hidden the seam. That is precisely why this is the cheapest moment to make the split: once SOPs, tiles, reports and Access tokens are all cross-referenced by department name, re-keying them is a project.
Quality is a function, not a department. It stays inside Administration's bundle for now
— which is what ISO 9001:2015 actually asks for at this size — but it gets its own function
tags (quality_hold, ncr_authority, sop_quality_owner) so it can be handed to a real
person, or spun into a department, without a registry redesign. That also answers the
prerequisite flagged in ai-across-departments: the quality agent acts on the quality
functions, held today by Administration.
What changes in the file (built 2026-09-13)¶
departments.yml gained a top-level functions: block: admin_work, engineering_work,
production_work, purchasing_work, sales_work, finance_work (one per current
department's duties — ERP group, access tokens, tiles, reports, SOPs, assistant skills),
plus sandbox_rehearsal (the access: [sandbox, chat-sandbox] grant every working
department shared, factored out once instead of six times), plus the seven
segregation-of-duties tags that grant nothing: po_create, po_approve, price_change,
inventory_adjust, quality_hold, ncr_authority, sop_quality_owner — all assigned to
admin today. Each of the six departments now carries functions: [...] plus only what
still belongs to the department directly: title and assistant.enabled (the admin page's
toggle edits that line textually, so it has to stay literal per department).
Resolution is a union, computed in one place: ~/.local/bin/_roles_resolve.py
(resolve_departments()), imported by onboard, ~/assistant/app.py, forms.py,
tasks.py, ~/.local/bin/sop-audit, reports-sync and sop-index — every reader now
resolves through it instead of reading the raw file. onboard functions lists each function
and which department(s) hold it. readonly and guest were left untouched, exactly as
planned — they carry no functions: key and resolve to what they already had.
Verified, not just reasoned about: ~/.local/bin/roles-verify prints every department's
fully resolved grants as sorted text; captured before the edit and after, diff between the
two is empty. onboard access-sync (no --yes, so read-only) reports every governed app "in
sync" — zero drift, confirming Cloudflare Access membership is unchanged for both real
people. Re-run with roles-verify > /tmp/a.txt && roles-verify > /tmp/b.txt && diff /tmp/a.txt /tmp/b.txt
after any future edit.
Segregation of duties, honestly¶
With two people we cannot separate purchasing from payment approval, or shipping from invoicing. So the control is evidence, not separation, and the function tags are what make it enforceable later without restructuring:
- flag every manual inventory adjustment and price change for owner review after the fact — an exception report, not a gate
- dual sign-off above a dollar threshold, once there is a second person who could give it
- the audit trail is the compensating control, which is an argument for the task register and the event log rather than a separate compliance exercise
The order functions get added, 2 → 10 people¶
| When | Function split out | From |
|---|---|---|
| Now | the SoD tags: po_create vs po_approve, price_change, inventory_adjust |
nowhere — they do not exist yet |
| ~5 people | Materials / planning (buyer-planner) | Purchasing and Production |
| ~8 people | Document control — revisions, ECO/ECN routing, SOP versioning as a duty | Quality and Engineering |
| ~10 people | Shipping and receiving as its own gatekeeper | Production |
| ~10–15 | Program management — Sales closes it, PM owns the running build | Sales |
| ~15+ | a standalone Quality department, and test/NPI engineering | only then, and only if volume or a customer contract demands it |
Nothing on that list needs doing today except the first row. The value of writing it down is knowing which additions are expected and therefore not a reason to redesign anything.
Moved out (2026-10-19, to keep this note under 400 lines)¶
Text moved verbatim; nothing was removed.
- roles-and-skills-assistant-design — skills by department, the accounting gap, WhatsApp options, "Making the assistant hard to misuse" (four walls, threats, red-team suite, kill switch), revised phases.
- roles-and-skills-gaps-and-reuse — twelve unanswered gaps, what we reuse and skip, which model and why Haiku, keeping the bill small, the local option.
- roles-and-skills-features — guided forms, guide mode, ranked employee helpers, the Engineering skill (part from a link or datasheet), keeping conversations, new and changed SOPs, the SOP backlog.
- roles-and-skills-sandbox-findings — the nine findings of 2026-09-12: sandbox skills, links and permissions, admin layout, guests, feedback, the admin Claude chat.
- roles-and-skills-log — Log entries from 2026-09-12 and 2026-09-13 (implementation history, incidents, root-cause fixes).
Log¶
Older entries (2026-09-12 and 2026-09-13) are in roles-and-skills-log; the 2026-09-17 entries stay here.
2026-09-17 — Employee action routing deployed¶
Task 241 first slice is live in assistant commit 3731348 (implementation 0ba5f47). Each Sales, Purchasing, Production and Engineering action family receives its authorized service credential; confirmation rechecks current registry roles and rejects unknown action types. Existing ownership and expiry checks remain. Ten focused regressions and all 45 local tests passed without model or ERP calls; service restart and health readback succeeded. Work used one gpt-5.6-sol worker at a time with independent orchestration review. No CLI cost was reported.
Remaining: department credentials do not enforce an employee's individual InvenTree permission overrides. Task 241 stays open; no stock receiving/count/transfer tools were added. SOP-010 v1.3 records this boundary. No new ERP records, stock movements, permission grants, emails or financial transactions were made for validation.
2026-09-17: LCSC review portal deployed in assistant b368624 at /purchases/match. Administration confirms historical matches; Purchasing/Engineering may view/search. Uses the existing service and identity gate. Operational details and limitations: business-operations-automation.