# AI Employees — current local inspection

24 September 2026. This is new feature coverage, separate from the original 36-course completion count and the existing AI Assistant course.

## Actual Studio navigation and read-only results

Normal local sign-in used **Email → Continue with Password → Password → Sign In**. Current Studio resolves the organization from the email; its ordinary credentials step did not ask for Site Name. This differs from the separately tested Vibe login.

Opened **AI, IVR, Automation → AI Employees**. The same sidebar lists **AI Assistant** separately. AI Employees is not under CRM in the current Studio sidebar.

The actual screen showed **Staff that are AI — their access, their work, their approvals**, with **Employees**, **Approvals**, **All work**, **Instructions**, **Templates** and **Settings** tabs, **Refresh** and **New AI employee**. The training organization had no employees, no queued/in-progress work, no approvals and $0 recorded AI spend.

- Employees: **No AI employees yet**.
- Approvals: **Nothing is waiting for approval**.
- All work: filters All, Queued, In progress, Needs approval, Done, Failed, Cancelled, Check-ins; empty queue.
- Templates: explanatory copy and **New template**, with no visible template cards in this organization.
- Settings: Company profile, Knowledge base, Defaults for new AI employees, Escalation. Company/knowledge/escalation were inherited and not set. Effective defaults showed **Inherited · Platform default**, **Stub**, around-the-clock/UTC, $5 daily budget, and approval requirements for delete, bulk messages, money, users/permissions.
- Instructions: actual GET returned500 repeatedly; UI remained Loading during the inspected interval. This is not recorded as successful instructions acceptance.

[Tab captures and read-only request statuses](assets/ai-employees-local/read-only-tabs.json). Screenshots are alongside that file. No employee was created, provisioned, switched on, assigned work or sent a chat during this inspection.

## Installed API mismatch diagnosed before creating anything

The installed runtime is older than current TypeScript source. Its controller constructor injects `AIInstructionService`; current source injects `AIEmployeeTeamService`, reads instructions from the configuration service and defines the team routes.

Actual local `GET /ai-employees/instructions` failed with **MongoInvalidArgumentError: Collection name must be a String**. The server stack points to installed `ai-instruction.service.js:26`, where `repo.find` receives `DataType.ai_instruction`. Current source no longer contains that service. Actual `GET /ai-employees/team` returned404: installed controller has the dynamic `GET(':name')` route but no static team route, unlike current source.

List, approvals, work, templates and config returned200. This is evidence of a mixed old-runtime/new-frontend checkout; it does not prove the current source's instructions/team implementation is broken. Draft creation is held until a coordinated runtime installation can preserve other local repairs and avoid starting autonomous workers/queue activity. No API restart was performed for this feature.

## Source findings for the dedicated course

`appengine/src/ai-employee` manages a distinct AI employee identity, user access, job queue, work reports, approval policy, hours/budget and runtime handover. The existing AI Assistant course covers assistant configuration/tool invocation; it does not cover this lifecycle.

Provider choices are **stub**, **session-manager**, and **external**. The stub explicitly records that it did no real work. The session-manager provider has a real provisioning/status contract; `projects/ai-employee` implements a separate runtime that signs in as the employee and runs jobs through Claude Code. Therefore a blanket claim that all AI Employees are stubs is inaccurate. Provider choice, provisioned identity, actual connection and worker results must each be checked.

Current source starts a new hire as a draft with a locked AI-only user. Access is separate from the job description. Provisioning the session-manager provider starts an external process, and switching on unlocks the user and schedules work; neither is a harmless read. Creating a draft can create/join the organization's AI team workspace and emit internal workspace notifications. These boundaries must be respected in a walkthrough.

Recommended tutorial sequence: understand employee versus assistant; inspect company instructions/knowledge/defaults; create a fictional draft with a specific role; verify its locked user and minimal access; choose and verify its runtime; set hours, budget and approval boundaries; activate deliberately; give one bounded task; inspect work steps and result; exercise approval/rejection; pause and verify access/work stops; supervise multiple employees through the team workspace. Each later stage still needs actual acceptance evidence.

## Current isolated API and draft walkthrough

A separate local API on3313 was prepared with the current AI Employee modules and temporary review isolation. Only the browser's AI Employee requests were routed there; ordinary application requests continued on3312. Its private queues have consumers disabled, and review guards reject provisioning, activation, work and leasing. These are test-environment restrictions, not claims about normal product behavior. See [isolated runtime verification](ai-employees-isolated-runtime.md).

With current source, Instructions returned200 and displayed three inherited platform instructions plus no organization instructions. Templates returned six platform roles: Receptionist, Sales follow-up, Support triage, Accounts receivable, Bookkeeping assistant, Social media. Each card offers **Hire** and **Copy**. No template was hired or copied.

Normal BusinessMade sign-in also passed on its existing local3600 service. Expanded **Admin**, selected **AI employees**, and reached `/admin/ai-employees` with the same six tabs. [Actual BusinessMade navigation](assets/ai-employees-local/businessmade-admin-entry.png).

### One deliberately inactive training hire

Opened **New AI employee** in Studio. The form contains Name, Job title, Handle, Reports to, Job description, Listens to, Working hours & budget, and What needs a person's OK. It explicitly says **It starts as a draft. Give it access, then switch it on.** Runtime is inherited from defaults, not selected in this creation form.

Entered **Tutorial Project Coordinator**, job title **Training project coordinator**, handle `tutorial-coordinator-20260924`, and a fictional no-execution training description. Left supervisor unset and extra listeners empty; set daily budget0, retained one concurrent job/two attempts, and retained person approval for delete, bulk messages, money and users/permissions. Submitted **Create** once: actual201.

- Employee ID: `6ab57f022c1f517002f93f4e`.
- User ID: `6ab57f022c1f517002f93f4c`.
- Actual record status: **draft**; user **locked:true**, groups **AI**, roles **AI**.
- Access response: no additional groups, mandatory **AI** group retained. The screen's “No groups yet” refers to additional permission groups; it does not mean the user lacks its AI identity.
- Runtime inherited **session-manager**, not provisioned; **Never connected**, **Not switched on yet**.
- Every work/approval/completion/failure queue count remained zero; daily budget0.
- Hiring created private **AI team** workspace `6ab57f022c1f517002f93f50`, with two members shown in Settings. Notifications were not delivered: isolated queue consumers were disabled.
- Full browser reload returned one draft card; reopening Profile preserved name, job description, UTC hours, zero budget and approval settings.

No Provision, Switch on, handover, Chat or Give work action was invoked. No access was granted. Keep this record as a locked training draft; do not recreate it when resuming.

[Before Create](assets/ai-employees-local/draft-before-create.png) · [Created setup](assets/ai-employees-local/draft-created-setup.png) · [Profile](assets/ai-employees-local/draft-profile.png) · [Locked access](assets/ai-employees-local/draft-access-locked.png) · [Reopened after reload](assets/ai-employees-local/draft-reopened.png) · [Sanitized readback](assets/ai-employees-local/draft-readback.json) · [Current Instructions](assets/ai-employees-local/instructions-current-runtime.png) · [Current Templates](assets/ai-employees-local/templates-current-runtime.png).

### Settings runtime label defect

Actual configuration readback has `defaults.runtime.provider: session-manager`. Settings nevertheless showed **Stub**, including during the initial inspection: its display used a ternary that labelled every provider other than external as Stub. Fixed `ai-employee-settings.tsx` to use the existing `PROVIDER_LABEL` mapping. This changes presentation only; configuration and the draft provider were not modified. The initial Stub screenshot is evidence of this defect, not a trustworthy statement of the actual configured runtime. Live post-fix verification follows separately.

Post-fix visual verification passed in the actual current Studio after its coordinated restart: Settings now displays **Session manager — provisioned and run there**, matching the configuration's provider; no erroneous **Stub** label remains. [Correct current settings](assets/ai-employees-local/settings-current-runtime.png). The words describe the selected runtime type, not completion of this draft's provisioning: the draft itself remains never connected and unprovisioned. TSX transpilation reported zero syntax errors.

## Continued setup: persisted access, hours and instructions

The original draft was reused. In **Overview → Change access**, selected **Reviewer**, saved, and reloaded the browser: the Reviewer chip persisted and login remained locked. Removed Reviewer using the same control; the final access readback retained only the mandatory AI identity. [Saved access](assets/ai-employees-local/access-reviewer-saved.png).

In **Edit → Working hours & budget**, turned off **Works around the clock**, retained Monday–Friday, entered **10:00–16:00**, and chose **America/Chicago**. Saved with daily budget0, concurrency1, attempts2. All four policies under **What needs a person's OK** remained **Ask a person first**. Readback confirmed the schedule. [Hours and approvals](assets/ai-employees-local/hours-approval-edit.png) · [Saved profile](assets/ai-employees-local/hours-approval-saved.png).

In global **Instructions → Add instruction**, entered **Training review boundaries** and a fictional review-only instruction. Selected **Only these**, then the **Tutorial Project Coordinator** pill, and saved. The saved instruction ID is `f26saz9s`, enabled, scoped to `tutorial-coordinator-20260924` only. The employee's Instructions tab displays **From platform**, **Your organization**, and **Just Tutorial Project Coordinator**; the scoped instruction appears in the organization layer. A full browser reload preserved the title, content and selected employee. [Before save](assets/ai-employees-local/instruction-scoped-before-save.png) · [Effective instructions](assets/ai-employees-local/instruction-effective-for-draft.png) · [Reloaded instruction](assets/ai-employees-local/instruction-reloaded.png).

Inspected, then cancelled, the conditional money approval option: selecting Allow reveals **…but still ask above** and the hint **0 means never ask.** This inspection did not save a policy change. The draft still requires human approval. [Threshold control](assets/ai-employees-local/money-threshold-unsaved.png).

## Owner controls with a separate manual lifecycle fixture

The isolated runtime guard was expanded for the separate `tutorial-lifecycle-20260924` fixture only. The original coordinator remained locked and inactive. Manual work below used actual server identities, state transitions and owner UI, but did not represent model execution.

- Opened **Approvals**, used **Refresh** to load the waiting request, opened its summary, and selected **Approve**. The toast says **Approved — it will carry on**. The work remained In progress until the manual worker resumed and completed it. [Waiting request](assets/ai-employees-local/manual-approval-waiting.png) · [Completed result](assets/ai-employees-local/manual-completed-job.png).
- Opened a deliberately failed two-attempt rehearsal in **All work**. The timeline shows the first failure returning to the queue and the second becoming Failed. **Put back in the queue** is available; it was not clicked. [Failure timeline](assets/ai-employees-local/manual-failed-job.png).
- Selected **Pause** on the employee. Actual UI showed **Paused** and **Login locked while paused**. A recent **Online** presence badge can remain: recent contact is separate from permission to work. The worker agent confirmed both existing token and fresh login were refused. [Paused and locked](assets/ai-employees-local/manual-paused-locked.png).
- After controlled reactivation, selected **Reject** on a second fictional approval. Toast: **Rejected — it will not do that**. The job initially remained In progress with a rejection event; the worker then resumed and completed explicitly without action. Rejection is a decision returned to the worker, not an automatic completed status. [Rejected decision](assets/ai-employees-local/manual-rejected.png).
- Opened a queued rehearsal and selected **Cancel job**. The drawer changed to **Cancelled**, recorded the owner cancellation, and offered **Put back in the queue**. No retry was requested. [Cancelled work](assets/ai-employees-local/manual-cancelled.png).

### Give work and Send exercised through the actual form

With the manual employee paused, opened **Give work**. The form explicitly warns **Not working right now (Paused.) — this waits in its queue.** It has one instruction textarea whose first line names the job, attachment controls **Records**, **From files**, **Upload**, visibility **Public**, priority options and **Send**. Submitted a fictional no-action assignment with no attachments. The drawer closed; the employee showed one queued job. Opened that job and selected **Cancel job**: status Cancelled and queue count0. No worker leased it.

[Filled assignment form](assets/ai-employees-local/give-work-before-send.png) · [Queued assignment](assets/ai-employees-local/give-work-queued.png) · [Cancelled assignment](assets/ai-employees-local/give-work-cancelled.png).

## Genuine isolated model result

A separate empty-data runtime subsequently ran one bounded inline fictional checklist through the real employee identity, briefing, lease and Claude worker. The worker's completed record is `6ab5a2817393e89fda21a4df`. Actual Studio **All work → Actual model: review an inline fictional checklist** displayed Done, the answer identifying the untested contact form and lost-lead risk, reasoning/note/completion events, and rounded cost **$0.04** (recorded $0.0350992). This is distinct from the earlier manual rehearsals. It did not contact anyone or use customer records. The isolated provider was stopped and the employee paused afterward. [Paused overview](assets/ai-employees-local/actual-model-paused.png) · [Completed model result](assets/ai-employees-local/actual-model-completed.png). See [actual model runtime proof](ai-employees-actual-model.md) and [manual lifecycle verification](ai-employees-manual-lifecycle.md); this does not claim broad production integrations were exercised.

### Genuine model approval, owner rejection and automatic resume

Created a second task through **Give work → Send** while paused: **Actual model: ask before recording a fictional decision**, ID `6ab5a3447393e89fda21a4e7`. The isolated worker genuinely requested approval and stopped at WAIT. In actual **Approvals**, opened **May I record the fictional launch checklist as approved?**, inspected its timeline, and selected **Reject**. This UI has no decision-note input; no note was invented or submitted through another route.

The real worker automatically resumed its same session and completed: **The owner rejected the fictional proposal to record the launch checklist as approved. I did not approve anything and performed no action.** Actual UI showed Done, the rejection and resumed timeline, and rounded cost$0.05 (recorded $0.0479462). The provider was stopped and employee paused with budget0 afterward. [Model approval request](assets/ai-employees-local/actual-model-approval-waiting.png) · [Completed after rejection](assets/ai-employees-local/actual-model-rejection-completed.png).

### Record attachment through the actual picker

Opened **Give work → Records**. The picker first chooses a collection; searching **Task** selected the built-in task collection. In its table, searched **Fictional launch checklist attachment**, ticked that exact training record, then selected **Attach**. The composer displays an attachment card and an optional **Say what this is for** comment separately from the task instructions. Left the comment empty and submitted the bounded read-only task with **Send**.

Actual queued work `6ab5a4537393e89fda21a4f4` contains one `kind:record`, datatype `task`, ID `6ab5a4287393e89fda21a4f3`, matching the selected fictional record. Its summary includes status/date only; the verification marker was not placed in instructions or attachment comments. [Attached record before Send](assets/ai-employees-local/attachment-before-send.png). Worker read/result verification follows in the separate runtime report.

The original draft remains draft/locked, AI-only, zero budget, zero jobs; its saved working hours and approval policy are in [continued setup readback](assets/ai-employees-local/continued-setup-readback.json).

The actual model completed the attached-record task: **Verification marker: LANTERN-4827. Missing checklist check: the contact form submit test. Page title and mobile layout are already checked. I only read the attached record and changed nothing.** Actual UI showed Done, the task attachment, result, timeline and rounded$0.03 cost (recorded$0.028183). The marker existed only inside the fictional task, not the prompt or attachment summary. [Model attachment result](assets/ai-employees-local/actual-model-attachment-completed.png). Exact proxy request/record immutability checks are in the [runtime evidence](ai-employees-actual-model.md).

After all three genuine model tasks, the provider was stopped and the manual employee was paused with budget0. [Final paused, locked overview](assets/ai-employees-local/actual-model-final-paused.png). The successful runs also exposed an authoring issue in the worker's instructions: bare `./ae step` guidance led to rejected note calls (**A step needs text**) in model reasoning. Completion itself succeeded; the worker reviewer is addressing the command guidance separately. This should not be hidden by describing every reasoning event as a successfully persisted note.

Attachment preparation boundary: the actual Task picker exposes search, List/Grid/Tree, pagination, Change collection, Cancel and Attach. It has **no Create/Add/New record control**. The fictional fixture was prepared through the owner's real API by the runtime reviewer; beginner instructions must explain that prerequisite or use a separately verified record-creation lesson. Do not invent a CRM Tasks menu or imply Attach creates a task. [Actual picker controls](assets/ai-employees-local/task-picker-controls.png).

## Managed Provision and Check provider through the actual UI

The dedicated manual fixture's runtime was changed through the real owner profile API to `{ "runtime": { "provider": "session-manager", "model": "sonnet" } }`; the original coordinator and organization defaults were untouched. There is no runtime selector in the actual employee Edit drawer. New hires inherit the configured organization default.

Reopened the employee to refresh its detail (the top-level Refresh had left the open detail's old provider visible). **Connection** now showed session-manager/not provisioned. Selected **Provision** against a separate empty isolated manager: the provider received the employee, assigned its external ID, and the setup advanced to **Next: switched on**. Selected **Check provider**. Actual UI returned **Session manager: the agent is there · running** and **locked: Its login is locked — switch it on in AI Employees.** The employee remained Paused/Never connected with no queued work and zero budget.

This verifies managed handoff and provider status, not an activated employee: the provider process can exist while the employee's login is locked. The reviewer then explicitly stopped the isolated provider. [Before provision](assets/ai-employees-local/managed-before-provision.png) · [Actual provider check](assets/ai-employees-local/managed-provider-checked.png). Exact backend/manager proof and the developer-only preparation are in [runtime verification](ai-employees-actual-model.md).

## File attachment preflight

**From files** opens File Manager, whose toolbar includes Upload and Add folder or file. The composer also has a direct Upload control. Source confirms the direct control uploads to the `presentation` location and then requests a file URL. Its initial **Public** label means public upload: checking the adjacent checkbox changes it to **Private**. This privacy choice applies to new uploads.

No upload was performed during this preflight. The worker reviewer found that the existing storage destination is external and is preparing an isolated path before a live file exercise. The record attachment success above must not be presented as proof that a file upload or download succeeded.

### Private upload performed after storage verification

After the storage metadata endpoint was corrected and verified, the authorized app walkthrough uploaded one plainly fictional tiny text file. Opened **Give work**, checked **Public** so the label became **Private**, selected **Upload**, and chose `fictional-launch-checklist.txt`. The attachment appeared as Document. Left its optional comment empty, entered the bounded read-only instructions, and selected **Send** while the employee was paused.

Actual job `6ab5a755b71e20a3a9111a62` was queued with one file attachment and a resolved private download URL. The URL is not reproduced in this documentation. The file is in the training organization's presentation location on its configured development storage; this was a real upload, not a fabricated local response. [Private file attached before Send](assets/ai-employees-local/private-upload-before-send.png). Actual worker reading and cleanup follow in the runtime report.

For the final bounded file exercise, the reviewer prepared only the manual fixture with Reviewer access, a temporary budget1, and no other open work. Selected **Switch on** through the actual owner UI: status changed to **Working**, the action became **Pause**, and Access showed **Login unlocked (working)**. Setup advanced to **signed in and said hello**, while the deliberately stopped provider had not connected yet. This proves activation and provider connection are separate steps. [Actual Switch on result](assets/ai-employees-local/managed-switched-on.png).

The private document exercise completed through the genuine managed worker. Actual UI displayed Done; attached `fictional-launch-checklist.txt`; result marker **MAPLE-7392** and the missing contact-form submit check; persisted note **Reviewed the private training document**; and rounded cost$0.04 (recorded$0.035042). The fixed note command was therefore exercised by a real model turn. [Completed private file task](assets/ai-employees-local/actual-model-private-file-completed.png).

Reopened the employee: Paused and login locked. **Connection → Check provider** returned **Session manager: the agent is there · stopped**, **stopped: Stopped here.** [Final paused employee](assets/ai-employees-local/managed-final-paused.png) · [Stopped provider](assets/ai-employees-local/managed-provider-stopped.png). The setup checklist still displayed **Working — every step is done.** while paused: its historical setup completion must not be read as current employee activity; this misleading label has been reported for correction.

### Setup label correction

Changed `ai-employee-setup.tsx` from **Working — every step is done.** to **Setup complete — every step is done.** The wording now describes completion of setup without claiming the paused employee is currently working. TSX transpilation reports zero diagnostics. Live verification is coordinated with the private attachment link repair to use one Studio restart.

### Final installed UI verification

After one coordinated Studio restart, **Connection** displayed **Setup complete — every step is done.** while the employee remained Paused. [Corrected setup label](assets/ai-employees-local/setup-complete-paused-fixed.png).

The private attachment link repair also passed in the actual installed UI: opened the completed private-document work item and clicked its filename button. It requested a fresh signed URL and opened a new browser tab whose file request returned **200**. The text contained **MAPLE-7392** and the fictional checklist. The browser screenshot contains only page content, not the signed address. [Opened private document](assets/ai-employees-local/private-document-opened.png) · [Sanitized HTTP proof](assets/ai-employees-local/private-document-opened.json).

All requested UI verification in this review is complete. The original coordinator is still a locked draft with no work, and the lifecycle fixture is paused with budget0 and its isolated provider stopped. Runtime cleanup and exact source regression evidence remain in the separate runtime report.

### UTF-8 rendering follow-up completed

After the storage content-type correction, reuploaded the identical tiny training text through **Give work → Private → Upload** to the same path, then closed the composer without sending. Opened the existing completed job's attachment again. Actual new-tab GET returned **200**, **Content-Type: text/plain; charset=utf-8**; **FICTIONAL TRAINING DOCUMENT — no customer information** rendered with the correct em dash, and marker MAPLE-7392 was unchanged. Updated the opened-document screenshot and sanitized HTTP proof above. No new job or model run was created.
