Skip to content

What the assistant can do, against what a person can do

Every route in this MES that changes something, and whether the floor assistant can perform it on somebody's behalf. Either a tool sends it, or the row says why not - and the reason is a rule, not a to-do.

Generated by fsmes assist coverage --markdown on 2026-10-11. Do not edit it by hand: tests/test_every_write_route_has_a_tool_or_a_reason.py regenerates it and fails if this file differs.

There are 77 write routes; 73 of them a screen calls; 55 write tools send one.

  • operator: 17 of 73 screen actions the assistant can propose; 14 with a walk; 16 by written reason.
  • supervisor: 33 of 73 screen actions the assistant can propose; 25 with a walk; 16 by written reason.
  • admin: 57 of 73 screen actions the assistant can propose; 42 with a walk; 16 by written reason.
  • agent: 24 of 73 screen actions the assistant can propose; 21 with a walk; 16 by written reason.

A screen action is a write route some page script calls: the things a person can do by pointing at them. The route's own capability gate says whether a role may perform it, and a walk is a "Show me" surface in services/assistant.py - a tool without one can be proposed but not shown, and is listed in assistant.WITHOUT_A_WALK with where its control is.

Every write route

Method Path Screen Tool, or why not Capability Walk
DELETE /admin/roles/{code} admin.js no tool — a judgement call, decided against. create_role and update_role exist because defining what a role grants is work an admin can be helped with and is reversible from the screen that did it. Deleting one is not: every account holding it loses every capability at once, and what it granted is not recoverable from the row that is gone. The screen keeps it; the assistant walks an admin to it. users.manage —
DELETE /maintenance/rules/{code} maintenance.js remove_dispatch_rule maintenance.plan no
PATCH /dashboard/config/{domain}/settings/{key} ai.js, config.js write_plant_setting per key: the define of the ConfigSection the key is listed under yes
PATCH /maintenance/rules/{code} maintenance.js change_dispatch_rule maintenance.plan no
POST /adjustments adjustments.js propose_adjustment adjustments.propose yes
POST /adjustments/{code}/approve adjustments.js no tool — the human in the loop before a PLC write - the whole point of the adjustment queue. propose_adjustment proposes; approving is the person the queue exists to ask. adjustments.approve —
POST /adjustments/{code}/reject adjustments.js no tool — rejecting is that same judgement, and an agent that could reject could quietly clear the queue it filled. adjustments.approve —
POST /admin/roles admin.js create_role users.manage yes
POST /assist/agent ai.js, assist.js no tool — the same: the conversation endpoint both agents answer on - the panel's floor assistant, and the AI tab's exploration. none —
POST /assist/agent/confirm assist.js no tool — "Do it" - the person's confirmation of a proposal. The loop reads it; a tool that pressed it would be the assistant confirming itself. none —
POST /assist/agent/decline assist.js no tool — the other half of that confirmation, for the same reason. none —
POST /assist/ask assist.js no tool — this is how the assistant is asked. A tool for it would be the assistant calling itself. none —
POST /auth/login app.js no tool — signing in is how a person becomes the person this all acts for. The tools sign in as AGENT themselves, in code, with no model in the loop. none —
POST /auth/logout app.js, common.js no tool — ending a person's session is not an action on the plant, and an assistant that could sign somebody out could sign them out mid-shift. none —
POST /auth/users admin.js create_user users.manage yes
POST /auth/users/{code}/password admin.js no tool — an agent never types a password. Setting somebody's is a person at a keyboard with the account holder in front of them, and a password that passed through a model's context is a password to rotate. users.manage —
POST /coa/pallet/{serial} coa.js issue_pallet_certificate quality.close_nc no
POST /coa/{order} coa.js issue_certificate quality.close_nc no
POST /design/chat design.js no tool — the design partner, which is how this product is built rather than how a plant is run. Not a floor action. audit.read —
POST /documents assist-record.js, instructions.js draft_instruction, create_document documents.write yes
POST /documents/{code}/approve/{revision} instructions.js no tool — putting a revision in force is the approver's signature, recorded against them by name. Decision 0035: the agent role holds documents.write and never documents.approve, and a confirmed "Do it" runs as AGENT on the person's behalf - so a tool here would sign for them. The assistant drafts (create_document, revise_document) and walks them to Approve. documents.approve —
POST /documents/{code}/revise instructions.js revise_document documents.write yes
POST /documents/{code}/withdraw instructions.js no tool — taking an instruction out of force is the same signature in reverse, gated on the same documents.approve, and withdrawn procedure is what the floor is working to until somebody notices. A person does it. documents.approve —
POST /equipment/downtime-reasons machine.js, reasons.js, station.js draft_downtime_reason process.define yes
POST /equipment/downtime-reasons/{code}/approve/{revision} — no tool — the vocabulary every stop from now on is named with. draft_downtime_reason drafts it; process.approve is a person's, and the walk to the pending-approvals panel is the honest answer. process.approve —
POST /equipment/{code}/state station.js set_machine_state equipment.state yes
POST /equipment/{code}/stops/label machine.js no tool — naming a stop is somebody's account of why a machine stopped, and nothing in the record says why - the MES watched the interval and the vocabulary is a list of candidates. A tool here would let a model choose between six reasons from the shape of the data and write the answer into the pareto as though a person on the floor had said it, which is how a plant's downtime analysis becomes fiction with a confidence interval. House rule 3: unlabelled data is reported as unlabelled. The honest assistant answer is to show which stops are unnamed and let whoever was there say. Another system's label has its own front door (fsmes inbound), and it records who claimed it. equipment.state —
POST /erp/outbox/{message_id}/retry ops.js erp_retry orders.close no
POST /execution/consume app.js, station.js issue_material production.consume yes
POST /execution/lots app.js, station.js create_lot production.consume yes
POST /execution/report app.js, station.js book_output production.book yes
POST /maintenance/corrective machine.js, maintenance.js raise_corrective_maintenance maintenance.perform yes
POST /maintenance/dispatch maintenance.js dispatch_maintenance maintenance.perform no
POST /maintenance/orders/{code}/assign machine.js, maintenance.js assign_maintenance_order maintenance.plan no
POST /maintenance/orders/{code}/complete machine.js, maintenance.js, station.js perform_maintenance maintenance.perform yes
POST /maintenance/orders/{code}/start machine.js, maintenance.js, station.js perform_maintenance maintenance.perform yes
POST /maintenance/plans machine.js, maintenance.js create_maintenance_plan maintenance.plan yes
POST /maintenance/raise maintenance.js raise_due_maintenance maintenance.perform yes
POST /maintenance/rules maintenance.js write_dispatch_rule maintenance.plan no
POST /masterdata/equipment admin.js, app.js, masterdata.js create_equipment masterdata.write yes
POST /masterdata/materials admin.js, coa.js, masterdata.js, orders.js create_material masterdata.write yes
POST /masterdata/materials/{code}/bom masterdata.js add_bom_component masterdata.write yes
POST /masterdata/personnel masterdata.js add_person users.manage no
POST /masterdata/routings admin.js create_routing masterdata.write yes
POST /quality/checks app.js, quality.js, station.js record_check quality.record yes
POST /quality/counts — record_count quality.record no
POST /quality/gauges gauges.js register_gauge masterdata.write no
POST /quality/gauges/{code}/calibrate gauges.js calibrate_gauge quality.close_nc no
POST /quality/nonconformances/{code}/close quality.js close_nonconformance quality.close_nc yes
POST /quality/nonconformances/{code}/disposition quality.js disposition_nonconformance quality.close_nc yes
POST /quality/nonconformances/{code}/review quality.js review_nonconformance quality.close_nc yes
POST /quality/samples — record_sample quality.record no
POST /quality/severities severities.js draft_nc_severity quality.define yes
POST /quality/severities/{code}/approve/{revision} — no tool — the same again for the severities a non-conformance is graded at - the second vocabulary, and deliberately the same reason word for word: quality.approve is a person's signature. quality.approve —
POST /quality/specs app.js, masterdata.js, quality.js, spc.js, station.js create_spec masterdata.write yes
POST /scheduling/calendar/exceptions schedule.js add_calendar_exception scheduling.plan yes
POST /scheduling/calendar/shifts schedule.js add_shift scheduling.plan yes
POST /scheduling/plan schedule.js plan_all_orders scheduling.plan yes
POST /scheduling/plan/{code} schedule.js plan_order scheduling.plan yes
POST /trace/units trace.js produce_units production.book yes
POST /trace/units/batch trace.js produce_batch production.book no
POST /trace/units/pack trace.js pack_unit production.book no
POST /trace/units/{serial}/status trace.js set_unit_status quality.close_nc no
POST /triggers triggers.js draft_trigger triggers.write yes
POST /triggers/{code}/approve triggers.js no tool — a trigger in force is logic acting on a live plant with nobody in the loop after it. Decision 0035 gives the agent triggers.write and never triggers.approve; draft_trigger drafts, a person signs. triggers.approve —
POST /triggers/{code}/withdraw triggers.js no tool — withdrawing a trigger stops a machine being protected by it. Gated on triggers.approve for that reason, and not a tool for the same one. triggers.approve —
POST /workorders app.js, orders.js, schedule.js create_order orders.create yes
POST /workorders/{code}/cancel orders.js order_action orders.close yes
POST /workorders/{code}/close app.js, orders.js order_action orders.close yes
POST /workorders/{code}/hold orders.js order_action orders.close yes
POST /workorders/{code}/operations/{seq}/complete orders.js, station.js complete_operation production.book yes
POST /workorders/{code}/operations/{seq}/start station.js start_operation production.book yes
POST /workorders/{code}/release app.js, orders.js create_order, order_action orders.release yes
POST /workorders/{code}/resume orders.js order_action orders.close yes
PUT /admin/roles/{code} admin.js update_role users.manage yes
PUT /admin/users/{code}/role admin.js assign_role users.manage yes
PUT /masterdata/personnel/{code}/home-equipment masterdata.js set_home_equipment users.manage no

Who holds what

Whether each role may perform the route at all, from the route's own capability gate. A role that may not perform it is never offered the tool either: what the assistant offers comes from agent.NEEDS, which is a second copy of the same answer, and test_every_write_tool_names_the_capability_its_route_demands fails if the two ever disagree about any role in this table. Until 2026-09-27 they disagreed about nine tools, and this column was the half that was right.

Action Capability operator supervisor admin agent
DELETE /admin/roles/{code} users.manage no no yes no
DELETE /maintenance/rules/{code} maintenance.plan no no yes no
PATCH /dashboard/config/{domain}/settings/{key} per key: the define of the ConfigSection the key is listed under no no yes yes
PATCH /maintenance/rules/{code} maintenance.plan no no yes no
POST /adjustments adjustments.propose no yes yes yes
POST /adjustments/{code}/approve adjustments.approve no no yes no
POST /adjustments/{code}/reject adjustments.approve no no yes no
POST /admin/roles users.manage no no yes no
POST /assist/agent none yes yes yes yes
POST /assist/agent/confirm none yes yes yes yes
POST /assist/agent/decline none yes yes yes yes
POST /assist/ask none yes yes yes yes
POST /auth/login none yes yes yes yes
POST /auth/logout none yes yes yes yes
POST /auth/users users.manage no no yes no
POST /auth/users/{code}/password users.manage no no yes no
POST /coa/pallet/{serial} quality.close_nc no yes yes no
POST /coa/{order} quality.close_nc no yes yes no
POST /design/chat audit.read no yes yes yes
POST /documents documents.write no yes yes yes
POST /documents/{code}/approve/{revision} documents.approve no no yes no
POST /documents/{code}/revise documents.write no yes yes yes
POST /documents/{code}/withdraw documents.approve no no yes no
POST /equipment/downtime-reasons process.define no no yes yes
POST /equipment/downtime-reasons/{code}/approve/{revision} process.approve no no yes no
POST /equipment/{code}/state equipment.state yes yes yes yes
POST /equipment/{code}/stops/label equipment.state yes yes yes yes
POST /erp/outbox/{message_id}/retry orders.close no yes yes no
POST /execution/consume production.consume yes yes yes yes
POST /execution/lots production.consume yes yes yes yes
POST /execution/report production.book yes yes yes yes
POST /maintenance/corrective maintenance.perform yes yes yes yes
POST /maintenance/dispatch maintenance.perform yes yes yes yes
POST /maintenance/orders/{code}/assign maintenance.plan no no yes no
POST /maintenance/orders/{code}/complete maintenance.perform yes yes yes yes
POST /maintenance/orders/{code}/start maintenance.perform yes yes yes yes
POST /maintenance/plans maintenance.plan no no yes no
POST /maintenance/raise maintenance.perform yes yes yes yes
POST /maintenance/rules maintenance.plan no no yes no
POST /masterdata/equipment masterdata.write no no yes no
POST /masterdata/materials masterdata.write no no yes no
POST /masterdata/materials/{code}/bom masterdata.write no no yes no
POST /masterdata/personnel users.manage no no yes no
POST /masterdata/routings masterdata.write no no yes no
POST /quality/checks quality.record yes yes yes yes
POST /quality/counts quality.record yes yes yes yes
POST /quality/gauges masterdata.write no no yes no
POST /quality/gauges/{code}/calibrate quality.close_nc no yes yes no
POST /quality/nonconformances/{code}/close quality.close_nc no yes yes no
POST /quality/nonconformances/{code}/disposition quality.close_nc no yes yes no
POST /quality/nonconformances/{code}/review quality.close_nc no yes yes no
POST /quality/samples quality.record yes yes yes yes
POST /quality/severities quality.define no no yes yes
POST /quality/severities/{code}/approve/{revision} quality.approve no no yes no
POST /quality/specs masterdata.write no no yes no
POST /scheduling/calendar/exceptions scheduling.plan no no yes no
POST /scheduling/calendar/shifts scheduling.plan no no yes no
POST /scheduling/plan scheduling.plan no no yes no
POST /scheduling/plan/{code} scheduling.plan no no yes no
POST /trace/units production.book yes yes yes yes
POST /trace/units/batch production.book yes yes yes yes
POST /trace/units/pack production.book yes yes yes yes
POST /trace/units/{serial}/status quality.close_nc no yes yes no
POST /triggers triggers.write no yes yes yes
POST /triggers/{code}/approve triggers.approve no no yes no
POST /triggers/{code}/withdraw triggers.approve no no yes no
POST /workorders orders.create yes yes yes yes
POST /workorders/{code}/cancel orders.close no yes yes no
POST /workorders/{code}/close orders.close no yes yes no
POST /workorders/{code}/hold orders.close no yes yes no
POST /workorders/{code}/operations/{seq}/complete production.book yes yes yes yes
POST /workorders/{code}/operations/{seq}/start production.book yes yes yes yes
POST /workorders/{code}/release orders.release yes yes yes yes
POST /workorders/{code}/resume orders.close no yes yes no
PUT /admin/roles/{code} users.manage no no yes no
PUT /admin/users/{code}/role users.manage no no yes no
PUT /masterdata/personnel/{code}/home-equipment users.manage no no yes no