Docs / Org Introspection
ADR-014

Org Introspection. Two layers, one assistant.

FlowMason ships two introspection layers so the assistant knows about your org's automation, not just its schema. INV-1 reads on-demand at request time. INV-2 harvests nightly into a snapshot table the assistant searches via tool-calling.

INV-1. On-demand per-turn excerpt

FMOrgIntrospector.scopedExcerpt(objectApiNames) reads Tooling at request time and threads a per-turn org-context excerpt into the LLM prompt. Capped at orgChatManifestMaxKb (default 8 KB ≈ 2000 tokens).

What's in the excerpt

  • Triggers per SObject + their active state
  • Active flows referencing the SObject
  • Validation rules + error messages (formulas optional)
  • Dependency degree (how many components reference this one)
  • Perm-set FLS labels for the SObject's fields

Enable

anonymous Apex
// INV-1 master switch (per-turn introspection)
FMConfig.set('orgChatManifestEnabled', 'true');

// Optional tuning
FMConfig.set('orgChatManifestMaxKb', '8');           // hard cap (default 8 KB)
FMConfig.set('orgChatManifestIncludeFormulas', 'false'); // formulas off by default

Tooling auth

Primary: Named Credential callout:FMTooling bound to a Named Principal w/ View All Setup. Fallback: session-id (engages automatically when the NC is absent). Pure-Apex collectors. No external service.

The 6 collectors

  • FMObjectsCollector — describe + custom-object metadata
  • FMApexCollector — Apex classes + triggers per SObject
  • FMFlowsCollector — active FlowDefinitionView rows
  • FMValidationRulesCollector — validation rules + active flag
  • FMDependencyCollectorMetadataComponentDependency degree
  • FMPermSetFlsCollector — perm-set FLS labels

INV-2. Nightly inventory harvest

FMOrgInventoryHarvester walks 5 component types in a single Queueable transaction (refactored from chained Queueables which hit the 5-deep dev-org cap). Stores results in FM_Org_Inventory_Snapshot__c. The assistant searches it via the inventory_search tool.

What it harvests

  • ApexClass — Tooling REST
  • ApexTrigger — Tooling REST (captures TableEnumOrId)
  • FlowDefinitionView — standard SOQL under SYSTEM_MODE (NOT a Tooling sObject)
  • ValidationRule — Tooling REST (captures EntityDefinition.QualifiedApiName)
  • PermissionSet — standard SOQL under SYSTEM_MODE

Schedule + first run

anonymous Apex
// Assign the operations permset to the service user
// (one-time): FlowMason_Org_Chat_Inventory_Admin

// Schedule nightly cron (default 0 0 2 * * ? *)
FMOrgInventoryScheduler.ensureScheduled();

// Force first harvest now (don't wait for the cron)
String jobId = FMOrgInventoryHarvester.enqueue();
System.debug('Harvest job: ' + jobId);

// Verify rows landed
System.debug([
  SELECT Component_Type__c, COUNT(Id) c
  FROM FM_Org_Inventory_Snapshot__c
  GROUP BY Component_Type__c
]);

Cron + single-flight

  • Default cron: 0 0 2 * * ? * (02:00 UTC daily). Override via FM_Config.orgChatInventoryCron.
  • Single-flight key on local.FMLLMCache (invharvinflight, 900s TTL) prevents duplicate enqueue.
  • Per-run Snapshot_Run_Id__c tags rows for the cleanup finalize() step.
  • Telemetry: FlowMasonRun__e with Action__c = org_inventory_harvest_completed + per-type row counts.

Common failure modes

F1. "sObject type 'FlowDefinitionView' is not supported"

Cause: someone reverted the routing. Tooling REST doesn't expose FlowDefinitionView or PermissionSet; both must go through standard Database.query under SYSTEM_MODE. Fix in harvestType.

F2. System.AsyncException: Maximum stack depth has been reached

Cause: chained-Queueable harvester re-introduced. Chain depth cap is 5 in dev orgs. Single execute() walks all types in one transaction.

F3. Schedulable blocks redeploy

Symptom: "Cannot delete this class because it is referenced elsewhere." Recovery:

anonymous Apex
// Recovery — abort cron, redeploy, reschedule
for (CronTrigger ct : [
  SELECT Id FROM CronTrigger
  WHERE CronJobDetail.Name = 'FM Org Inventory Nightly'
]) {
  System.abortJob(ct.Id);
}
// sf project deploy start --target-org Flowmason \
//   --source-dir force-app/main/default/classes/FMOrgInventoryHarvester.cls
FMOrgInventoryScheduler.ensureScheduled();

F4. Inventory stale / partial run

Force a fresh harvest: FMOrgInventoryHarvester.enqueue();. Single-flight cache prevents duplicate enqueue.

F5. Single-flight cache wedge

Symptom: enqueue() returns same job id repeatedly. Evict manually:

Cache.OrgPartition p = Cache.Org.getPartition('local.FMLLMCache');
p.remove('invharvinflight');

Disable

No MDT switch — disablement is operational. Abort the cron:

for (CronTrigger ct : [
  SELECT Id FROM CronTrigger
  WHERE CronJobDetail.Name = 'FM Org Inventory Nightly'
]) {
  System.abortJob(ct.Id);
}

The inventory_search tool degrades to "inventory not yet harvested" when the table is empty.

Related

  • Org Chat — the primary consumer
  • Tool-calling — how inventory_search + lookup_metadata get exposed to the model