# AI ticket tools: persisted local acceptance

Verified 24 September 2026. **12 checks passed.** This closes the local persisted tool read/update rehearsal; it is not a live model-driven ticket conversation or a notification-delivery test.

The runner loads the installed compiled `GetTicketTool`, `UpdateTicketTool`, `TicketsService` and `UsersService`. It reads the existing fictional organization owner and signed-in training customer from the local database. Staff authorization uses the actual persisted user, group and role resolution methods. A small organization-scoped Mongo adapter supplies repository reads and writes; it does not run the complete RepositoryService mutation pipeline. The notification command bus is replaced by a recording sink, so the update requested one notification but sent none. No provider/model request was made.

## What passed

- The training customer retrieved their own ticket by number. Internal comments did not appear in the tool result.
- That customer could not retrieve another customer's ticket by number or by supplying the other customer's email.
- Staff retrieved the customer's ticket using its number alone, without impersonating the customer's email.
- The customer could not use the staff update tool.
- Supplying an `authenticatedActor` inside tool parameters did not replace a missing trusted caller.
- A real persisted user with no grants was refused, even when its caller snapshot claimed the Owner role. The current stored roles controlled access.
- All refused requests left the ticket unchanged; there were zero repository writes before the authorized staff update.
- Staff changed fictional ticket **AIPROOF24** to `resolved` and added “Fictional saved update verified locally.” A fresh database read confirmed both fields; exactly one ticket update occurred.
- A normal customer sign-in followed by the live HTTP ticket-read endpoint returned that saved resolution. Internal notes and comments were absent from the customer response.
- The same HTTP endpoint returned 403 for another customer's email and 401 for an anonymous caller.

## Retained local fixtures

`AIPROOF24` is the customer's resolved fictional ticket. `AIPROOF25` belongs to another fictional email and remains new. A fictional user with no roles or groups was retained for the no-grants check. These records carry `data.reviewMarker = TICKET-TOOL-PERSISTED-20260924`; no existing ticket was altered. The runner refuses to create duplicate fixtures if that marker already exists.

## Evidence and limits

[Machine-readable results](assets/ai-local/ticket-persisted-acceptance.json) and [runner](assets/ai-local/ticket-persisted-acceptance.cjs).

The authenticated HTTP read verifies the real running server can read the result with its own repository and ownership checks. The update was a direct compiled tool/service integration with an adapted persistence boundary and a notification sink. It does **not** prove a model selected the ticket tools, an assistant preserved the caller through a full model conversation, a browser chat rendered the answer, the full repository update pipeline ran, or any notification was delivered. The separate provider approval covers customer search only, so this rehearsal did not expand the assistant's enabled tools or call its model test endpoint.
