Skip to content

Collaboration: Expense Review routine (mail-triggered, reviews approval requests against guideline documents) - #3211

Merged
ryanweiler92 merged 4 commits into
devfrom
collaboration-expense-routine
Oct 26, 2026
Merged

ryanweiler92 merged 4 commits into
devfrom
collaboration-expense-routine

Conversation

@ryanweiler92

Copy link
Copy Markdown
Collaborator

Summary

Adds a new system routine, Expense Review, to Collaboration Routines. When an email whose subject starts with Meeting Approval or Business Approval reaches the owner's inbox, the routine reads the request, checks it against the guideline documents the owner uploaded, and emails the owner a Deloitte-styled HTML memo with a suggested decision. It never replies to the requester; the owner still responds in MES.

Unlike the daily brief and weekly agendas, this routine runs off the mail import instead of a schedule, so WorkRoutine learns about unscheduled kinds.

What changed

New routine kind

  • WorkExpenseRoutine (id expense-review, table WORK_EXPENSE_ROUTINE, automation expense-review.automation.json, result node review).
  • Settings: only enabled and timezone. Subject prefixes are a constant (SUBJECTS). Matching is case-insensitive, skips leading gateway tags such as [EXT], and never matches RE:/FW: replies.
  • Guideline documents live in a fixed user-asset folder, collaboration/routines/e8f3a1c2-5b6d-4a7e-9c0f-1d2e3b4a5f60, and are copied into the run room's documents/ folder before the agent starts (same path custom routines use). WorkRoutineDocuments.requireRoutine accepts that id besides stored custom routine ids.
  • Reactors: WorkGetExpenseRoutine(), WorkSaveExpenseRoutine(settings=[{enabled, timezone}]), WorkRunExpenseRoutine(threadId=[...], messageKey=[...]).
  • Automation: gather (Python, reads each request thread as clean text) -> review (Collaboration agent with collaborationRoutine, documents in the room) -> deliver (Python, Deloitte email shell, decision paragraph highlighted, Outlook link back to the request). Review order per request: Suggested decision, Before you respond, Guideline check table, Request table.

Trigger path

  • BrainMailImport.Run.inbound records [messageKey, threadId, subject] of each newly imported message to the owner (from someone else, or a note the owner sent themselves; toMe now covers a self sender on To/Cc).
  • BrainSync.run and BrainSync.importHeaders (webhook and pull paths) call WorkExpenseRoutine.onNewMail after the ingest lock. Matching mail from one import batch becomes one run. Only mail Brain has never seen triggers; mail that arrived while the routine was off is never reviewed.
  • WorkRoutine.trigger(kind, owner, pixel, delay) fires the routine's durable Quartz job with a per-firing Pixel (JobDataMap override; RunPixelJobFromDB reads the merged map and SchedulerResource.executePixel runs the posted Pixel). It calls SchedulerDatabaseUtility.startScheduler first, because Quartz stands by after a restart until a schedule is saved.
  • WorkExpenseRoutine.run settles a lost run first, claims with now=true, and when another review is still running schedules a one-shot retry two minutes later.

WorkRoutine engine

  • Kind.scheduled() = the kind has a time default. Unscheduled kinds skip time/days validation, keep a never-firing cron (0 0 0 1 1 ? 2099) so save still creates the durable job and recipes row, report no nextRunAt, and get their own failed() wording.
  • run(...) overload that merges trigger-supplied inputs (thread_ids, message_keys).
  • Comment on the once-per-day guard shortcut: when restored, it must exclude unscheduled kinds.

Daily inbox brief

  • WorkInboxRoutine.inputs passes skip_reply_subjects = WorkExpenseRoutine.SUBJECTS; the brief's gather skips reply drafts for those threads (new _request helper strips Re:/Fw:/bracket tags). The brief must not draft an approval of spend; the request still shows in priorities and action items. New start global in inbox-daily-brief.automation.json.

Plumbing

  • CollaborationOwlCreator table loop and CollaborationDbUtils unique owner index include WORK_EXPENSE_ROUTINE.
  • WorkRoutineRoom.open maps the new id (open a result as a chat).
  • BrainResetUtils deletes the routine's runs.

@ryanweiler92 ryanweiler92 self-assigned this Oct 26, 2026
@ryanweiler92
ryanweiler92 requested a review from a team as a code owner October 26, 2026 17:27
@snyk-io

snyk-io Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

kunal0137 and others added 3 commits October 26, 2026 13:28
…d-only memory

- Routine runs get their own baseline (CollaborationPrompts.routinePrompt) instead of the chat prompt, with where the answer goes per delivery; the run message now carries only name, time, documents, skills and task.
- Routines can save email drafts with SaveDraft (never sent, Settings draft folder); chats are refused it.
- Memory is read only in routines: Remember and Forget are left out and refused.
- Delivery reaches the room as the server-owned collaboration_routine_delivery option.
- Routine guide and CreateRoutine allow naming who a draft is for.
@ryanweiler92
ryanweiler92 merged commit 445b04f into dev Oct 26, 2026
4 checks passed
@ryanweiler92
ryanweiler92 deleted the collaboration-expense-routine branch October 26, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants