> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Terminology

> Core terms used across AI experience, as well as internal ServiceTitan terminology.

export const CartoTerminologyGlossary = () => {
  const SECTIONS = React.useMemo(() => [{
    id: "systems",
    title: "Systems",
    description: "Platform and design-system layers that frame AI Kit and the Carto theme.",
    columns: true
  }, {
    id: "agentic-framework",
    title: "Agentic framework",
    description: "Shared language for autonomy, oversight, and agent-authored experiences.",
    columns: false
  }], []);
  const TERMS = React.useMemo(() => {
    const GUIDE = "https://service-titan-ai-playbook.vercel.app";
    return [{
      id: "agentos",
      title: "AgentOS",
      section: "systems",
      definition: "ServiceTitan's platform layer for agentic products: the agents, tools, memory, and surfaces they run in. Carto is the design system scoped to AgentOS.",
      seeAlso: [{
        id: "carto-carto-ds",
        label: "Carto"
      }, {
        id: "atlas",
        label: "Atlas"
      }]
    }, {
      id: "anvil2",
      title: "Anvil2",
      section: "systems",
      definition: "Core design system for product chrome and non-AI surfaces. Carto is a peer, not a replacement.",
      seeAlso: [{
        id: "carto-carto-ds",
        label: "Carto"
      }]
    }, {
      id: "atlas",
      title: "Atlas",
      section: "systems",
      definition: "ServiceTitan's customer-facing agentic assistant, built on Carto.",
      seeAlso: [{
        id: "carto-carto-ds",
        label: "Carto"
      }]
    }, {
      id: "carto-carto-ds",
      title: "Carto",
      section: "systems",
      definition: "Design system for agentic experiences and Gen UI. A peer to Anvil2, scoped to AgentOS and agentic surfaces.",
      seeAlso: [{
        id: "agentos",
        label: "AgentOS"
      }, {
        id: "anvil2",
        label: "Anvil2"
      }, {
        id: "gen-ui-generative-ui",
        label: "Gen UI (Generative UI)"
      }]
    }, {
      id: "agentic-experience",
      title: "Agentic experience",
      section: "agentic-framework",
      definition: "A product experience where AI acts on your behalf, not only advises.",
      seeAlso: [{
        id: "atlas",
        label: "Atlas"
      }]
    }, {
      id: "artifact",
      title: "Artifact",
      section: "agentic-framework",
      definition: "A durable AI work product (document, diff, draft) that matters more than the chat that produced it.",
      guideUrl: `${GUIDE}/patterns/artifacts`,
      seeAlso: [{
        id: "gen-ui-generative-ui",
        label: "Gen UI (Generative UI)"
      }]
    }, {
      id: "approval-gate",
      title: "Approval gate",
      section: "agentic-framework",
      definition: "A designed pause for human sign-off before an agent acts. Counts only when the proposal shows the real diff, recipients, or command, not a vague proceed prompt.",
      guideUrl: `${GUIDE}/patterns/approval-gates`,
      seeAlso: [{
        id: "hitl-human-in-the-loop",
        label: "HITL (Human-in-the-loop)"
      }, {
        id: "scoped-autonomy",
        label: "Scoped autonomy"
      }]
    }, {
      id: "blast-radius",
      title: "Blast radius",
      section: "agentic-framework",
      definition: "How much damage an agent's worst plausible action could cause before a human can intervene.",
      seeAlso: [{
        id: "reversibility",
        label: "Reversibility"
      }, {
        id: "scoped-autonomy",
        label: "Scoped autonomy"
      }]
    }, {
      id: "decision-trace",
      title: "Decision Trace",
      section: "agentic-framework",
      definition: "A readable account of why an agent took a specific action: what it considered, what it chose, and on what basis. Lets people audit or contest the call after the fact, not only see that something happened.",
      seeAlso: [{
        id: "working-transcript",
        label: "Working transcript"
      }]
    }, {
      id: "disambiguation",
      title: "Disambiguation",
      section: "agentic-framework",
      definition: "Clarifying ambiguous intent before an agent acts: present the competing readings, let the human choose, then proceed.",
      seeAlso: [{
        id: "agentic-experience",
        label: "Agentic experience"
      }, {
        id: "approval-gate",
        label: "Approval gate"
      }, {
        id: "scoped-autonomy",
        label: "Scoped autonomy"
      }]
    }, {
      id: "escalation",
      title: "Escalation",
      section: "agentic-framework",
      definition: "Handing a problem to a human at the edge of competence or authority, with what was tried and a recommendation, not improvising sideways.",
      guideUrl: `${GUIDE}/patterns/human-in-the-loop-escalation`,
      seeAlso: [{
        id: "hitl-human-in-the-loop",
        label: "HITL (Human-in-the-loop)"
      }]
    }, {
      id: "gen-ui-generative-ui",
      title: "Gen UI (Generative UI)",
      section: "agentic-framework",
      definition: "UI an agent authors in a live session (runtime), rather than at design or build time. Often uses JSON Rendering.",
      seeAlso: [{
        id: "carto-carto-ds",
        label: "Carto"
      }, {
        id: "json-rendering",
        label: "JSON Rendering"
      }]
    }, {
      id: "hitl-human-in-the-loop",
      title: "HITL (Human-in-the-loop)",
      section: "agentic-framework",
      definition: "A human must review or approve before an agent action takes effect. Real oversight requires a reviewable proposal, not a click-through.",
      guideUrl: `${GUIDE}/patterns/human-in-the-loop-escalation`,
      seeAlso: [{
        id: "role-agent",
        label: "Role Agent"
      }, {
        id: "approval-gate",
        label: "Approval gate"
      }, {
        id: "decision-trace",
        label: "Decision Trace"
      }]
    }, {
      id: "json-rendering",
      title: "JSON Rendering",
      section: "agentic-framework",
      definition: "When UI is rendered from a JSON spec against an approved component catalog, instead of hand-written React code. The `@servicetitan/json-render-react` package contains the component catalogs that are built with Anvil2 and the AI Kit.",
      seeAlso: [{
        id: "gen-ui-generative-ui",
        label: "Gen UI (Generative UI)"
      }, {
        id: "anvil2",
        label: "Anvil2"
      }, {
        id: "carto-carto-ds",
        label: "Carto"
      }]
    }, {
      id: "legible-memory",
      title: "Legible memory",
      section: "agentic-framework",
      definition: "Retained context shown in plain language that people can view, edit, and delete, announced when written, not discovered later.",
      guideUrl: `${GUIDE}/patterns/legible-memory`,
      seeAlso: [{
        id: "decision-trace",
        label: "Decision Trace"
      }]
    }, {
      id: "reversibility",
      title: "Reversibility",
      section: "agentic-framework",
      definition: "Whether an agent action can be undone. Autonomy scales with undo: wide latitude where mistakes reverse; hard gates where they don't.",
      guideUrl: `${GUIDE}/patterns/reversibility`,
      seeAlso: [{
        id: "hitl-human-in-the-loop",
        label: "HITL (Human-in-the-loop)"
      }, {
        id: "blast-radius",
        label: "Blast radius"
      }]
    }, {
      id: "role-agent",
      title: "Role Agent",
      section: "agentic-framework",
      definition: "An AI agent scoped to a specific job role: its responsibilities, tools, and autonomy bounds. Design it like briefing a junior colleague for that seat, not as a general-purpose chat.",
      seeAlso: [{
        id: "scoped-autonomy",
        label: "Scoped autonomy"
      }]
    }, {
      id: "scoped-autonomy",
      title: "Scoped autonomy",
      section: "agentic-framework",
      definition: "What an agent may do freely, what needs approval, and what's off-limits: set in advance, expanded deliberately.",
      guideUrl: `${GUIDE}/patterns/scoped-autonomy`,
      seeAlso: [{
        id: "agentic-experience",
        label: "Agentic experience"
      }, {
        id: "hitl-human-in-the-loop",
        label: "HITL (Human-in-the-loop)"
      }]
    }, {
      id: "streaming",
      title: "Streaming",
      section: "agentic-framework",
      definition: "Rendering model output as it is generated, token by token, instead of waiting for the full response. Turns wait time into signal and makes interruption a real control.",
      guideUrl: `${GUIDE}/patterns/streaming-response`,
      seeAlso: [{
        id: "working-transcript",
        label: "Working transcript"
      }]
    }, {
      id: "working-transcript",
      title: "Working transcript",
      section: "agentic-framework",
      definition: "A live record of agent actions while a task runs, so people can audit the process, not only the conclusion.",
      guideUrl: `${GUIDE}/patterns/working-transcript`,
      seeAlso: [{
        id: "decision-trace",
        label: "Decision Trace"
      }, {
        id: "legible-memory",
        label: "Legible memory"
      }, {
        id: "streaming",
        label: "Streaming"
      }]
    }];
  }, []);
  const normalize = value => value.toLowerCase().replace(/[-_/().]/g, " ").replace(/\s+/g, " ").trim();
  const renderInline = text => {
    const parts = text.split(/(`[^`]+`)/g);
    return parts.map((part, index) => {
      if (part.startsWith("`") && part.endsWith("`")) {
        return <code key={index} className="rounded bg-gray-100 dark:bg-neutral-800 px-1.5 py-0.5 text-[0.875em] font-medium text-gray-800 dark:text-gray-200">
            {part.slice(1, -1)}
          </code>;
      }
      return <span key={index}>{part}</span>;
    });
  };
  const [searchQuery, setSearchQuery] = React.useState("");
  const [activeSection, setActiveSection] = React.useState("all");
  const [guideOnly, setGuideOnly] = React.useState(false);
  const [filteredTerms, setFilteredTerms] = React.useState(TERMS);
  React.useEffect(() => {
    const query = normalize(searchQuery);
    setFilteredTerms(TERMS.filter(term => {
      if (activeSection !== "all" && term.section !== activeSection) {
        return false;
      }
      if (guideOnly && !term.guideUrl) {
        return false;
      }
      if (!query) {
        return true;
      }
      const titleMatch = normalize(term.title).includes(query);
      const idMatch = normalize(term.id).includes(query);
      return titleMatch || idMatch;
    }));
  }, [searchQuery, activeSection, guideOnly, TERMS]);
  const sectionCounts = SECTIONS.reduce((acc, section) => {
    acc[section.id] = TERMS.filter(term => term.section === section.id).length;
    return acc;
  }, {});
  const guideCount = TERMS.filter(term => term.guideUrl).length;
  const groupedTerms = SECTIONS.map(section => ({
    ...section,
    terms: filteredTerms.filter(term => term.section === section.id).sort((a, b) => a.title.localeCompare(b.title))
  })).filter(section => section.terms.length > 0);
  React.useEffect(() => {
    const hash = window.location.hash.replace(/^#/, "");
    if (!hash) return;
    const frame = window.requestAnimationFrame(() => {
      const el = document.getElementById(hash);
      if (el) {
        el.scrollIntoView({
          block: "start"
        });
      }
    });
    return () => window.cancelAnimationFrame(frame);
  }, [filteredTerms]);
  const clearLocationHash = () => {
    if (!window.location.hash) return;
    const {pathname, search} = window.location;
    window.history.replaceState(null, "", `${pathname}${search}`);
  };
  const goToTerm = termId => event => {
    event.preventDefault();
    const isVisible = filteredTerms.some(term => term.id === termId);
    if (!isVisible) {
      setSearchQuery("");
      setActiveSection("all");
      setGuideOnly(false);
    }
    window.location.hash = termId;
    if (isVisible) {
      window.requestAnimationFrame(() => {
        document.getElementById(termId)?.scrollIntoView({
          block: "start"
        });
      });
    }
  };
  const chipBase = "inline-flex items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm font-medium transition-colors";
  const chipIdle = "border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 dark:hover:border-gray-600 dark:hover:bg-gray-700";
  const chipActive = "border-gray-900 bg-gray-700 text-white hover:border-gray-800 hover:bg-gray-800 dark:border-white dark:bg-gray-200 dark:text-gray-700 dark:hover:border-gray-100 dark:hover:bg-gray-100";
  const countIdle = "rounded-full bg-gray-100 px-1.5 py-0.5 text-xs tabular-nums dark:bg-gray-700";
  const countActive = "rounded-full bg-gray-300 px-1.5 py-0.5 text-xs tabular-nums text-gray-700 dark:bg-gray-300 dark:text-gray-800";
  const allPressed = activeSection === "all" && !guideOnly;
  return <div className="my-8 text-gray-900 dark:text-gray-100">
      <div className="relative">
        <input type="text" placeholder={`Search ${TERMS.length} terms...`} value={searchQuery} onChange={e => {
    clearLocationHash();
    setSearchQuery(e.target.value);
  }} className="w-full rounded-lg border border-gray-300 bg-white px-4 py-3 text-gray-900 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-neutral-700 dark:bg-neutral-900 dark:text-white" aria-label="Search terminology" />
      </div>

      <div className="mt-4 flex flex-wrap items-center gap-2">
        <button type="button" onClick={() => {
    clearLocationHash();
    setActiveSection("all");
    setGuideOnly(false);
  }} className={`${chipBase} ${allPressed ? chipActive : chipIdle}`} aria-pressed={allPressed}>
          All
          <span className={allPressed ? countActive : countIdle}>
            {TERMS.length}
          </span>
        </button>
        {SECTIONS.map(section => {
    const pressed = activeSection === section.id && !guideOnly;
    return <button type="button" key={section.id} onClick={() => {
      clearLocationHash();
      setActiveSection(section.id);
      setGuideOnly(false);
    }} className={`${chipBase} ${pressed ? chipActive : chipIdle}`} aria-pressed={pressed}>
              {section.title}
              <span className={pressed ? countActive : countIdle}>
                {sectionCounts[section.id]}
              </span>
            </button>;
  })}
        <button type="button" onClick={() => {
    clearLocationHash();
    setGuideOnly(value => !value);
    setActiveSection("all");
  }} className={`${chipBase} ${guideOnly ? chipActive : chipIdle}`} aria-pressed={guideOnly}>
          Field Guide
          <span className={guideOnly ? countActive : countIdle}>
            {guideCount}
          </span>
        </button>
      </div>

      <div className="mt-3 text-sm text-gray-500 dark:text-gray-400">
        Showing {filteredTerms.length} of {TERMS.length} terms
      </div>

      <Info>
        Entries with a Field Guide link point to deeper pattern guidance. See
        the full{" "}
        <a href="https://service-titan-ai-playbook.vercel.app/guide" target="_blank" rel="noreferrer">
          AI Design Field Guide
        </a>
        .
      </Info>

      {groupedTerms.length === 0 ? <div className="mt-8 rounded-xl border border-dashed border-gray-300 px-6 py-12 text-center text-gray-500 dark:border-neutral-700 dark:text-gray-400">
          No terms found
          {searchQuery ? <> for "{searchQuery}"</> : null}. Try
          another filter or clear search.
        </div> : <div className="mt-10 flex flex-col gap-12">
          {groupedTerms.map(section => <section key={section.id} aria-labelledby={`section-${section.id}`} className="scroll-mt-28">
              <div className="mb-5 border-b border-gray-200 pb-4 dark:border-neutral-800">
                <h2 id={`section-${section.id}`} className="m-0 text-2xl font-semibold tracking-tight text-gray-900 dark:text-gray-100">
                  {section.title}
                </h2>
                <p className="mt-2 mb-0 max-w-3xl text-base leading-relaxed text-gray-600 dark:text-gray-400">
                  {section.description}
                </p>
              </div>

              <div className={section.columns ? "grid grid-cols-1 gap-4 md:grid-cols-2" : "flex flex-col gap-4"}>
                {section.terms.map(term => <article key={term.id} className="group flex h-full flex-col scroll-mt-24 rounded-xl border border-gray-200 bg-white p-5 shadow-sm transition-colors hover:border-gray-300 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:border-neutral-700">
                    <div className="flex flex-wrap items-start justify-between gap-3">
                      <h3 id={term.id} className="m-0 text-lg font-semibold tracking-tight text-gray-900 dark:text-gray-100">
                        {term.title}
                      </h3>
                      {term.guideUrl ? <a href={term.guideUrl} target="_blank" rel="noreferrer" className="inline-flex shrink-0 items-center gap-1 rounded-full border border-gray-200 bg-gray-50 px-2.5 py-1 text-xs font-medium text-gray-700 no-underline transition-colors hover:border-gray-300 hover:bg-gray-100 hover:text-gray-900 dark:border-neutral-700 dark:bg-neutral-900 dark:text-gray-200 dark:hover:border-neutral-600 dark:hover:bg-neutral-800 dark:hover:text-white">
                          Field Guide
                          <span aria-hidden="true">↗</span>
                        </a> : null}
                    </div>

                    <p className="mt-3 mb-0 flex-1 text-base leading-relaxed text-gray-700 dark:text-gray-300">
                      {renderInline(term.definition)}
                    </p>

                    {term.seeAlso?.length > 0 ? <div className="mt-4 flex flex-wrap items-center gap-2 border-t border-gray-100 pt-4 dark:border-neutral-800">
                        <span className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
                          See also
                        </span>
                        {term.seeAlso.map(item => <a key={item.id} href={`#${item.id}`} onClick={goToTerm(item.id)} className="inline-flex items-center rounded-full border border-gray-200 bg-gray-50 px-2.5 py-1 text-xs font-medium text-gray-700 no-underline transition-colors hover:border-gray-300 hover:bg-white hover:text-gray-900 dark:border-neutral-700 dark:bg-neutral-900 dark:text-gray-200 dark:hover:border-neutral-600 dark:hover:bg-neutral-800 dark:hover:text-white">
                            {item.label}
                          </a>)}
                      </div> : null}
                  </article>)}
              </div>
            </section>)}
        </div>}
    </div>;
};

If you haven't already, start with [Getting Started](/docs/kits/ai-kit/getting-started).

<CartoTerminologyGlossary />
