# Docs - [What is hcifootprint?](/): Turn a web app's interaction surface into a typed, traversable journey graph an LLM agent can plan over and act on — as the signed-in user, through the app's own buttons and handlers. - Get Started - [Quick start](/get-started/quick-start): Author the graph, connect it to your running app, serve it to any LLM host — the first two steps run offline with no API key. - [The three contexts](/get-started/three-contexts): Map, traversal, actions — the three questions an agent asks about your app, each answered in the same three parts: what you declare, what you wire, and what the agent gets. - [The adoption ladder](/get-started/adoption-ladder): Start in read-only guide mode with zero handlers, then register handlers, then serve an agent — one authored graph carries you the whole way. - [Demos](/get-started/demos): Two runnable demo apps in the repo plus the three-commit dress shop — every one runs with no API key, no network, and behaves identically every time. - The map: What can this app do? - **Declare it** - [The navigation graph](/map/navigation-graph): buildNavigationGraph turns the container tree you already picture — pages, areas, tabs, modals, tools — into a validated, frozen graph with typed node paths. - [Journeys & journey frames](/map/journeys): A journey is a named multi-step flow the planner commits to; the frame narrows the served space to the journey's steps plus a guaranteed escape. - [Guarded journeys](/map/guarded-journeys): A wizard whose steps sit behind guards, whose Next button is greyed rather than hidden, whose every step proves it happened, and whose route table doubles as the spine that keeps every page reachable. - **Or adopt what you already have** - [Graph sources & the merge order](/map/graph-sources): fromRoutes, fromReactRouter, fromJourneys and fromLiveStore turn the descriptions your app already owns into graph input — under one documented merge order, so nobody re-types anything. - **What the agent gets** - [Journeys as fixed tools (Mode B)](/map/modes): serveToAgent serves one tool per journey plus four fixed generics — the tool array never changes for the life of a conversation, so the prompt cache stays warm. - [The MCP server](/map/mcp): mcpServer(session) returns a standard @modelcontextprotocol/sdk Server — you pick the transport, and the SDK stays an optional peer imported only behind hcifootprint/mcp. - Traversal: Where am I, and how do I get there? - **Declare it** - [A destination the app mints](/traversal/minted-destinations): The cookbook for an action that creates a thing and then goes to its page. The address does not exist until the handler runs, so the claim is a page NAME and never an address — a half-address is not an address. - **Wire it** - [Sessions & fire()](/traversal/sessions): fire() is synchronous and honest — effectStatus says what is known at return time, whenSettled resolves once with the final truth, and a declared input contract is advertised and enforced. - [Presence & visibility](/traversal/presence): Registration observes MOUNTED, visibility is an explicit signal, and everything derived rather than observed carries an honesty marker. - **What the agent gets** - [Navigation — a claim is not an observation](/traversal/navigation-claims): An action that navigates declares no writes, so success looks like nothing happening. `goesTo` discloses the claim before the fire; `arrival` says whether an observation has corroborated it after — and there is no third value for *did not arrive*. - [How to reach a page](/traversal/how-to-reach): The fewest declared hops to a page, walked from the app's own navigation claims — a route, not a plan, and not a permission. - Actions: What is possible here? - **Declare it** - [Guards](/actions/guards): A guard is a flat, serializable filter over your projected state — read this before writing a state projector. - [Actuation & materialisation](/actions/actuation): The gesture lives ON the edge — url, click, tab, programmatic — and "can this actually be performed?" is computed from the real gesture, not just "is a handler registered?". - [A read is an action](/actions/reading-data): How the agent gets your app's data — declare a tool whose handler RETURNS it. The return rides `produced`, sanitized and capped, on the data channel; serving it is never a claim that the model used it. - **Wire it** - [Live bindings & fromLiveStore](/actions/live-bindings): Components register what they have when they render — and if your app keeps a live action store, fromLiveStore does the subscribe-and-register bookkeeping for you. - [The human sensor](/actions/human-sensor): A framework-free, record-only DOM sensor. The graph you already authored IS the instrumentation manifest — the app declares values, the sensor never reads them, and anything it cannot attribute is reported honestly or not at all. - [The React binding](/actions/react-binding): A skin over the human sensor — one hook per control, no reporting call in your app, and the value your component already holds handed over rather than read off the DOM. - [Contextful actions](/actions/contextful-actions): One wrapper at registration, and both doors into an action — the agent's fire and your app's own click — land in the same capture envelope. The anchor becomes bidirectional: it actuates for the agent and senses for the record. - **What the agent gets** - [What kind of edge am I holding?](/actions/reading-an-action-row): A reading guide for the served action row — every stamp, the declaration behind it, and what it is evidence of. There is no kind field, because the kinds compose: the kind of an edge IS the set of declarations it carries. - [When the world moved under the row](/actions/freshness-and-single-flight): The tier above disclosure: cite the row you planned against, declare what a control does when something it was offered under has since moved, and allow one occurrence at a time. All of it opt-in; none of it on by default. - [Who did it, who may, and how you would know](/actions/attribution-and-authority): Every transition says which rung filed it and what that is worth. Then three opt-in policies: refuse the guesses, refuse the wrong actor, and refuse a high-effect action nobody could check. - [What would free it](/actions/what-would-free-it): A greyed control that says which action would turn it on — derived from what the app already declares, never authored. - [What a control holds](/actions/what-a-control-holds): The draft already in the box, on the action row the model reads — declared by your app, read late, never scraped, and governed by the same redaction list as the payload it is about to become. - [When a control is busy](/actions/when-a-control-is-busy): The third state — the app's own words for "this one is working right now", on the action row a model reads. A label, never a flag; it gates nothing, and no clock in this library will ever expire it. - [Confirms & receipts](/actions/receipts): A high-effect ask carries receipts — what will happen, why it's fireable, where the human is — and by default the agent's own `confirm: true` crosses it; `requireHumanApproval` makes the crossing require an approval a person recorded. - [A pause is not a failure](/actions/paused-not-failed): A needs-confirm result carries `performed: false` and one authored sentence saying nothing has been done — and `did_it_work` takes the askId, so an agent can ask whether the human has decided instead of guessing. - [Whose decision it is](/actions/whose-decision-it-is): Some choices are the person's to make, not the agent's to perform. `humanDecides` says so on the control they answer through — disclosed on every surface, and enforced nowhere. - [Going async — the adoption recipe](/actions/going-async): Four moves, in the order most apps need them. Return the promise, name the fire, say you are working, ask later. Everything here is your app's own control flow — there is no async wire to adopt. - [Waiting for the app](/actions/waiting-for-the-app): The async story in one place — your handler's promise is the completion signal, a transitionId threads identity on the state rail, and the one served await has a ceiling that never becomes a verdict. - [When the app is still working](/actions/when-the-app-is-still-working): The work ledger — your app says what it is still doing, bound to the fire it belongs to. A row, never a latch: closing it settles nothing, and no clock here will ever close it for you. - [Ground truth — facts the model cannot argue with](/actions/grounding): groundTruth() is the app's own record of what was attempted and how each attempt came to rest, in words a model is told outrank the conversation — and it rides every whats_here result as `facts`. - Reference - [Tree-shaking & packaging](/reference/tree-shaking): True-ESM, sideEffects false, leaf modules — importing one helper ships half a kilobyte, and the repo's own test suite bundles the shipped dist to keep it that way. - [The drift harness](/reference/testing): hcifootprint/testing catches graph↔app drift in dev and CI — static lint over the graph alone, and a browserless driver for your interaction logic. - [Design lineage](/reference/design-lineage): The numbered design documents that decided the library's load-bearing shapes — what each locked in, and where to read the full reasoning. - [API Reference](/api)