Huddle is a sales employee agent that turns the notes from an internal deal-strategy meeting into real work, action items become Tasks assigned to the right internal people, and the strategy itself gets logged against the Opportunity so the reasoning behind a deal's plan isn't lost the moment the meeting ends.
Where Scribe handles the customer-facing side of a deal (what was said on a call with the prospect), Huddle handles the internal side: the deal review, the strategy session with a sales engineer or manager, the "how are we going to win this" conversation that currently lives only in whoever attended's memory.
Everything Huddle logs is internal-only at the data-model level, and every action it takes is written to a dedicated audit trail reviewable through its own app, tabs, LWCs and list views.
- Why Huddle
- Design goals
- The three surfaces
- Architecture and data flow
- The four agent topics
- Data model
- How extraction actually works
- Component inventory
- Guardrails & safety
- Install & deploy
- Testing
- Project structure
- License
Internal strategy conversations are some of the most valuable and least documented moments in a sales cycle. A rep, manager and sales engineer get in a room, work out a plan, who to loop in, what objection to prepare for, what the real timeline pressure is, and then that plan evaporates the moment the meeting ends unless someone manually writes it up and manually creates every follow-up task.
In practice that rarely happens consistently, which means:
- Action items get forgotten because they were only ever spoken out loud.
- The reasoning behind a strategy isn't visible to anyone who joins later, a new AE inheriting the account, a manager doing a pipeline review.
- The same ground gets re-litigated in a follow-up meeting because nobody wrote down what was already decided.
Huddle's job is to make the strategy meeting itself produce the documentation and task assignments as a byproduct, instead of requiring a separate write-up step nobody has time for.
| Goal | How Huddle honors it |
|---|---|
| Never invent action items or ownership | A sentence only becomes an action item when it carries both an actionable verb and a specific owner: a named person, a named role, or explicit first person singular. "We agreed to hold the discount" is a team decision with no individual owner, so it stays in the strategy summary and never becomes somebody's task. A name that came up in another context never makes that person an owner. |
| Confirm before assigning across people | Create Action Items is two-phase. apply=false returns the full numbered who-gets-what preview and writes nothing; apply=true runs only after the attendee confirms, and carries require_user_confirmation. Corrections go back in as ownerOverrides; rejected items go in as skipItems. |
| One set of notes, two outputs | The recap is parsed once by Log Strategy and cached on Huddle_Strategy_Log__c.Extraction_JSON__c. Every other topic reuses that single parse via the strategy log id, no re-pasting, no inconsistent re-parsing. |
| Preserve reasoning, not just outcomes | Reasoning-bearing sentences are extracted into their own field (Decision_Reasoning__c), separate from the decision. "We're holding the discount" without "because finance flagged margin risk" is the half a new AE can't use, and it's what makes the same debate happen twice. |
| Internal strategy stays internal | Strategy logs live on a dedicated object, not a shared Note or Activity, so nothing reading Opportunity activity history, including a customer-facing agent, can pick them up. Internal_Only__c is asserted on the record, not by convention. |
| Full transparency | Every write funnels through Huddle_ChangeLogService and produces a Huddle_Change_Log__c row, with who it was assigned to as a first-class column and a link back to the meeting recap that caused it. |
- The employee agent (conversational),
Huddle_Agent, an AgentforceAiAuthoringBundlewith four working topics plus a router and an off-topic handler. Internal-facing only, no MIAW or Experience Cloud deployment, since this is purely an internal sales tool. - The automation engine (headless), four autolaunched Flows, each wrapping an invocable Apex action that performs the write and logs the audit row as its final step.
- The Huddle app (review & audit), a Lightning app where managers and deal teams see every strategy session logged, every action item assigned, and every decision still unresolved across the team's pipeline.
Attendee recap ──▶ Huddle agent (Agentforce)
│
▼
Huddle_ExtractionService ── one structured parse ──▶ cached on Huddle_Strategy_Log__c
│
┌─────────────────┼──────────────────┬─────────────────────┐
▼ ▼ ▼ ▼
Log Strategy Create Action Items Identify Open Deal Team Digest
│ (preview → confirm) Decisions │
│ │ │ │
│ ▼ │ │
│ Huddle_OwnerResolver │ │
│ (resolve, or admit │ │
│ it can't) │ │
▼ ▼ ▼ ▼
Strategy_Log__c Task(s) Open_Decision__c (read-only)
│ │ │ │
└─────────────────┴──────────────────┴────────────────────┘
│
▼
Huddle_Change_Log__c ← every action, no exceptions
Takes the meeting recap and logs the strategy and the reasoning behind it against the Opportunity as an internal note, clearly separated from customer-facing activity history. Purely additive and assigns nothing to anybody, so it runs without a confirmation gate. This is the entry point: it produces the single extraction every other topic reuses.
Extracts every action item, along with who it's for and any deadline, and creates a Task per item. Because these Tasks are usually assigned to someone other than the person entering the notes, this topic always previews before writing:
1. "Priya will put together the security questionnaire response by Friday" → Priya Raman, due 3 Aug
2. "Dan Ortiz needs to reach out to their VP of Ops next week" → Dan Ortiz, due 7 Aug
3. "The sales engineer will build the demo environment next week" → unassigned (parked with you), due 7 Aug
[OWNER UNCLEAR: the recap named a role ("Sales Engineer") rather than a person]
Only after the attendee says yes does the second action run with apply=true.
Flags anything the meeting explicitly left unresolved ("we still need to decide X before the next call") as a distinct, trackable Huddle_Open_Decision__c rather than letting an open question quietly disappear into the general notes. Deduplicates against questions already open on the same deal, so re-running on the same recap doesn't pile up copies.
On request, summarizes the most recent strategy, its reasoning, the open action items with owners, and the unresolved questions, for anyone joining the deal later. Needs only an Opportunity id, no recap. Read-only against the deal, but still writes an audit row, because the guardrail is that every Huddle action produces one, without exceptions carved out for the convenient cases.
The raw meeting recap plus the extracted strategy, linked to the Opportunity and explicitly flagged internal-only.
| Field | Purpose |
|---|---|
Meeting_Recap__c |
The recap verbatim, so every extracted item traces to the words that produced it |
Strategy_Summary__c |
What the team decided to do |
Decision_Reasoning__c |
Why they decided it, the part that survives a handoff |
Internal_Only__c |
Always true; internal status asserted at the data-model level |
Extraction_JSON__c |
The single cached parse every downstream topic reads |
Low_Confidence__c |
Set when the recap was too thin to extract confidently |
Attendees__c, Meeting_Date__c, Logged_By__c, Source_Channel__c |
Meeting context |
An unresolved question, linked to the Opportunity and to the strategy log that raised it.
| Field | Purpose |
|---|---|
Question__c |
The question, lead-in stripped so it reads as the question itself |
Context__c |
The surrounding sentence: what it blocks, what would settle it |
Status__c |
Open / Resolved, so it can't stay implicit |
Needed_By__c |
Deadline the meeting attached, left blank rather than guessed |
Days_Open__c |
Formula driving the aging list view and dashboard rollup |
One record per action Huddle takes.
| Field | Purpose |
|---|---|
Change_Type__c |
Strategy Logged / Task Created / Decision Raised / Decision Resolved / Digest Generated |
Assigned_To__c |
Who the work landed on, first-class because cross-person assignment is the risk |
Source_Strategy_Log__c |
Traces every change back to the meeting recap that caused it |
Rep_Confirmed__c |
True when the attendee explicitly approved before the write |
Related_Record_Id__c, Object_API_Name__c, Opportunity__c, Detail__c |
What changed, where |
Huddle_Generated__c, Huddle_Source_Strategy_Log__c, Huddle_Owner_Unclear__c, so Huddle's action items stay separable from a rep's own tasks and ambiguity is visible on the record.
These are defined on Activity, not Task. Salesforce requires custom activity fields to be created on the shared Activity object; they then surface on Task (and Event), which is why the Apex reads Task.Huddle_Generated__c while the metadata and field-level security live under objects/Activity/.
Huddle_ExtractionService is the only place recap text is interpreted, which is what keeps the strategy log, the action items and the open decisions agreeing with each other. It classifies each sentence in a fixed order:
- Open questions first. "We still need to decide whether to send the revised quote" contains the verb send, but nobody committed to anything, so it must not become a task.
- Then action items, which need an actionable verb and a specific owner. This is the rule that stops group decisions turning into phantom assignments.
- Then reasoning and decisions, which can co-occur in one sentence and are captured into separate fields.
Huddle_OwnerResolver then maps owner hints to real users, and is deliberately willing to fail. A hint that matches nobody, matches several people, or is only a role comes back unresolved with a reason:
| Hint | Outcome |
|---|---|
"me" (from "I'll draft the MAP") |
Resolves to the note-taker |
"Dan Ortiz" matching one active user |
Resolves |
"Priya" matching three active users |
Unresolved, ambiguous, names the candidates |
"Sales Engineer" |
Unresolved, a role is not a person |
"Zzyzxqvist" matching nobody |
Unresolved, no active user matches |
Anything unresolved becomes a Task flagged Huddle_Owner_Unclear__c, parked with the note-taker, with the reason written into the Task description. Huddle never picks a plausible-looking user to make the unresolved count zero.
Custom objects — Huddle_Strategy_Log__c, Huddle_Open_Decision__c, Huddle_Change_Log__c, plus three custom fields on Activity (which surface on Task).
Apex (16 classes)
| Class | Role |
|---|---|
Huddle_ExtractionService |
The extraction/classification engine |
Huddle_MeetingParse |
The single structured parse, JSON-serializable for caching |
Huddle_OwnerResolver |
Owner hint → User, or an honest failure |
Huddle_ChangeLogService |
The audit backbone every write funnels through |
Huddle_LogStrategyInvocable |
Topic 1 |
Huddle_CreateActionItemsInvocable |
Topic 2, two-phase preview/apply |
Huddle_IdentifyOpenDecisionsInvocable |
Topic 3 |
Huddle_DealTeamDigestInvocable |
Topic 4 |
Huddle_ChangeLogConsoleController |
Console + badge controller (read-only) |
Huddle_HomeDashboardController |
Dashboard rollups (read-only) |
Huddle_Constants, Huddle_Util |
Shared vocabulary and coercions |
Huddle_TestUtil + 3 test classes |
Fixtures and coverage |
Flows (autolaunched, referenced by the agent) — Huddle_Log_Strategy, Huddle_Create_Action_Items, Huddle_Identify_Open_Decisions, Huddle_Deal_Team_Digest.
LWCs
huddleChangeLogConsole— filterable by Opportunity, rep and change type; each row links back to the source meeting recap.huddleHomeDashboard— sessions this week, action items completed/created, unresolved decisions, action items with an unclear owner (red when non-zero), plus open decisions aging past a week and a 7-day trend.huddleOpportunityStrategyBadge— "Huddle has logged 2 strategy sessions and 4 action items" on the Opportunity record page, with the internal-only status stated out loud and a click-through to the full history.
Lightning app — Huddle, with tabs: Huddle Home, Change Log, Strategy Logs, Open Decisions, Action Items (Tasks), Change Log records.
List views — Task: Created by Huddle, Huddle: Owner Unclear. Open Decision: Open, Aging Past 7 Days, All. Strategy Log: This Week, By Opportunity.
Permission sets — Huddle_Agent_User (write to Task, Strategy Log, Open Decision, Change Log; Opportunity deliberately read-only, Huddle logs strategy against a deal but never edits the deal's own fields) and Huddle_Reviewer (read access for managers, plus the ability to resolve an open decision).
- Every write produces a change-log row. Not optional, not configurable. A manager reviewing the app can always see exactly what was logged and assigned.
- Cross-person assignment requires confirmation every time. Assigning work to someone who wasn't the one entering the notes carries real risk of misattribution if the extraction gets it wrong, so the assignment list is always shown first and
require_user_confirmationis set on the applying action. - Ambiguity is surfaced, never resolved by guessing. No owner Huddle can resolve means a Task flagged "owner unclear" parked with the note-taker, with the reason on the record, and its own list view and dashboard tile so it doesn't sit unnoticed.
- Internal-only at the data model. A dedicated object, not a shared Note or Activity that another tool or customer-facing agent might read from.
- The agent is deployed internal-facing only.
AgentforceEmployeeAgent, no MIAW or Experience Cloud surface, since there's no external or partner-facing use case.
Requires the Salesforce CLI and an org with Agentforce enabled.
git clone https://github.com/VurtuoLabs/huddle.git
cd huddle
npm install
sf org login web --alias huddle-dev
sf project deploy start --target-org huddle-dev
sf org assign permset --name Huddle_Agent_User --target-org huddle-dev
sf org assign permset --name Huddle_Reviewer --target-org huddle-devThen, in the org:
- Open Setup → Agentforce Agents and activate
Huddle. - Add
huddleOpportunityStrategyBadgeto the Opportunity record page in the Lightning App Builder. - Open the Huddle app to see the dashboard and change log.
npm run lint # ESLint over the LWCs
npm run prettier:verify # formatting (XML is excluded, see below)
sf apex run test --target-org huddle-dev --code-coverage --result-format humanLast verified run: 35 tests, 100% pass rate, per-class coverage 80–100%.
The Apex tests concentrate on the negative cases, since those are what the design goals actually assert:
- a decision with no named owner produces zero action items
- an unresolved question is not also filed as an action item
apply=falsecreates no Tasks and no audit rows- an unresolvable or role-only owner is flagged, not guessed
- a thin recap sets
lowConfidenceinstead of producing confident nonsense
Note on formatting:
**/*.xmlis in.prettierignore. The Prettier XML plugin reflows text nodes inside elements whose content must be an exact API name (<flow>,<apexClass>), which corrupts the metadata. Apex, JS, HTML and CSS are Prettier-formatted normally.
force-app/main/default/
├── aiAuthoringBundles/Huddle_Agent/ # the Agentforce agent
├── applications/ # the Huddle Lightning app
├── classes/ # 16 Apex classes
├── flexipages/ # Huddle Home, Change Log
├── flows/ # 4 autolaunched flows
├── lwc/ # 3 components
├── objects/ # 3 custom objects + Activity fields + list views
├── permissionsets/ # Huddle_Agent_User, Huddle_Reviewer
└── tabs/ # 5 tabs
See LICENSE.