{"service":"mainmind","version":"0.4.0","generated_from":"src/surface.js","roles":["observer","steward","founder"],"notes":{"identity":"OAuth mounts carry {member, name, role, charter, tenant} as token props, stamped at /authorize when the person types their invite code. Roles are enforced at registration: a tool a role may not use is a tool that role's session never sees.","tenancy":"Every organization is a tenant. Every projected node, run, event, ask and member row carries a tenant slug, every read is scoped by it, and a replace-mode projection push can only ever delete its own tenant's rows. The product's own org is tenant 'hq' in the same database, tenant #2 is the vendor.","parity":"Every MCP tool has a plain-HTTP twin. A runner with no MCP support drives the identical contract with curl. Bring-your-own-agent is only honest if the transport is not a privilege."},"mcp":{"endpoint":"/mcp","transport":"streamable-http","auth":"oauth (invite code) or founder bearer","tools":[{"name":"whoami","roles":["observer","steward","founder"],"desc":"Who am I on this mount, identity, role, tenant, and how fresh the read is. Cheap; call it when unsure what this connection may do.","args":[],"returns":"One line of identity plus the projection's commit and freshness."},{"name":"boot","roles":["observer","steward","founder"],"desc":"Call this FIRST in any session that will do real work for the organization. Returns the organization's entry documents (ORG.md, AUTHORITY.md), your role charter, and how to work from this mount. Everything else routes from here.","args":[],"returns":"ORG.md, AUTHORITY.md, your charter, and the working rules, at a named commit."},{"name":"find_process","roles":["observer","steward","founder"],"desc":"Route a task to the organization's Process for it. Give the task in plain words ('a customer wants a refund', 'restock from a vendor'); returns the routing index plus closest matching Processes. Read the matched Process with read_node before acting.","args":[{"name":"intent","type":"string","required":true,"desc":"What you're trying to do, in plain words"}],"returns":"Closest matching Processes plus the routing index."},{"name":"read_node","roles":["observer","steward","founder"],"desc":"Read one document from the company file by path, e.g. 'processes/create-purchase-order.md' or 'records/systems/shopify.md'. Returns full content plus the commit it reflects. Cite the path when you use what you read.","args":[{"name":"path","type":"string","required":true,"desc":"Repo-relative path, as listed by search/find_process"}],"returns":"The full node at the projection's commit, never with third-party annotations mixed in."},{"name":"search","roles":["observer","steward","founder"],"desc":"Full-text search across the whole company file: processes, records, lessons, decisions, roles. Returns paths with snippets, follow up with read_node on the hits that matter.","args":[{"name":"query","type":"string","required":true,"desc":"Words to find, e.g. 'gst refund shiprocket'"},{"name":"kind","type":"string","required":false,"desc":"Limit to a kind: process | record | lesson | decision | role | system"}],"returns":"Ranked hits (BM25 + vector, fused, reranked) with snippets."},{"name":"run_start","roles":["steward","founder"],"desc":"Open a run on the live control plane at the START of real work, before doing it. This is what makes you visible to the whole team as currently working. Returns a run_id, carry it through run_heartbeat, emit_event and run_finish so everything you do is attributed to one run.","args":[{"name":"task","type":"string","required":true,"desc":"One plain-English sentence: what this run is setting out to do"},{"name":"actor_label","type":"string","required":false,"desc":"Who is working, as a person would say it. Defaults to your member identity"},{"name":"actor_id","type":"string","required":false,"desc":"Stable slug for the actor. Derived from the label if omitted"},{"name":"harness","type":"string","required":false,"desc":"claude-ai | claude-code | codex | byo | cron"},{"name":"process","type":"string","required":false,"desc":"The Process being run, e.g. 'reconciliation-run'"},{"name":"doing","type":"string","required":false,"desc":"The first step, short"},{"name":"scopes","type":"string[]","required":false,"desc":"repo: scopes this run holds, e.g. ['repo:processes/reconciliation-run.md']"}],"returns":"run_id"},{"name":"run_heartbeat","roles":["steward","founder"],"desc":"Report that a run is still alive and say what it is doing now. Call this as you move between steps. It is what the dashboard renders as the live line. A run that stops heartbeating shows as stalled, not working.","args":[{"name":"run_id","type":"string","required":true,"desc":"The run_id returned by run_start"},{"name":"doing","type":"string","required":false,"desc":"What is happening right now, one short phrase"},{"name":"scopes","type":"string[]","required":false,"desc":"Replace the scopes this run holds"}],"returns":"Acknowledgement."},{"name":"run_finish","roles":["steward","founder"],"desc":"Close a run. Use status 'landed' when the work is done, 'awaiting-ruling' when it is parked on a founder decision (the run keeps its branch and stays visible), 'conflict' when the target moved and the work must be rebuilt, 'failed' when it broke.","args":[{"name":"run_id","type":"string","required":true,"desc":"The run to close"},{"name":"status","type":"enum","values":["landed","awaiting-ruling","conflict","failed"],"required":true,"desc":"landed | awaiting-ruling | conflict | failed"},{"name":"outcome","type":"string","required":false,"desc":"One or two plain sentences on how it ended"},{"name":"proposal_ref","type":"string","required":false,"desc":"The branch carrying the diff, if one was pushed"}],"returns":"Acknowledgement with the terminal status."},{"name":"list_runs","roles":["observer","steward","founder"],"desc":"List runs on the live control plane: which are open right now (and what each is doing), and which recently ended.","args":[],"returns":"JSON: open runs (with derived staleness) and recent finished runs."},{"name":"emit_event","roles":["steward","founder"],"desc":"Report a real operational event from a run to the live control plane. Use after completing real work. Title must be one plain-English, buyer-readable sentence; no secrets, no repo paths. Pass run_id to attribute it to an open run.","args":[{"name":"type","type":"enum","values":["run","brief","judgment","ruling","deposit","note"],"required":true,"desc":"run=a process ran; brief=morning brief; judgment=needs the founder; ruling=founder ruled; deposit=lesson/record/amendment landed; note=anything else"},{"name":"title","type":"string","required":true,"desc":"One plain-English sentence, buyer-readable, no jargon, no secrets"},{"name":"detail","type":"string","required":false,"desc":"Optional 1-3 sentences of context"},{"name":"amount","type":"string","required":false,"desc":"Optional money figure involved, e.g. $1,240.00"},{"name":"needs_you","type":"boolean","required":false,"desc":"true if this waits on the founder"},{"name":"actor","type":"string","required":false,"desc":"Who did it, e.g. 'operator (claude code)'"},{"name":"run_id","type":"string","required":false,"desc":"The run this belongs to, from run_start"}],"returns":"Acknowledgement with the recorded timestamp."},{"name":"list_events","roles":["observer","steward","founder"],"desc":"List recent events from the live instance feed.","args":[{"name":"limit","type":"number","required":false,"desc":"max events, default 20"}],"returns":"JSON array of events, newest first."},{"name":"ask_founder","roles":["steward","founder"],"desc":"Put ONE decision to the Founder as a link with two buttons. Use when work hits something only the Founder may decide, an approval boundary, spend, a rule change. You do the reading and arguing first; what reaches the Founder is one question and what becomes true on yes. Returns a URL. It lands in the Founder's queue, and you can also paste it to them directly.","args":[{"name":"ask","type":"string","required":true,"desc":"The one question, plain words, answerable yes/no"},{"name":"becomes","type":"string[]","required":true,"desc":"Short lines: what becomes true if they say yes"},{"name":"because","type":"string","required":false,"desc":"One sentence of why now"},{"name":"cost","type":"string","required":false,"desc":"What it costs or gives up, one line"},{"name":"run_id","type":"string","required":false,"desc":"The run this belongs to"}],"returns":"The decision URL (/d/<key>), one question, two buttons, evidence attached."}]},"http":[{"method":"GET","path":"/api/health","auth":"public","desc":"Liveness and which channels of the runtime contract are open.","returns":"{ok, service, version, channels}. `channels` says which parts of the runtime contract are open."},{"method":"GET","path":"/api/surface","auth":"public","desc":"This surface, as JSON, the machine-readable twin of ARCHITECTURE.md. What the deployed worker actually speaks.","returns":"The whole surface as JSON: {service, version, roles, notes, tools[], http[]}."},{"method":"GET","path":"/llms.txt","auth":"public","desc":"Agent-readable index of the public docs (llms.txt convention).","returns":"text/markdown. The agent index of these docs."},{"method":"POST","path":"/api/access","auth":"public","desc":"Ask for access. Body: {email, note?} as JSON or as a plain form post. A repeat address updates the note rather than adding a row.","returns":"{ok} or {ok, already:true}. A form post redirects back to the landing with ?access=thanks or ?access=error."},{"method":"GET","path":"/api/access","auth":"bearer","desc":"The access requests waiting on you, newest first. The founder's own surface.","returns":"{requests[]} with created_at, email, note, source and status."},{"method":"POST","path":"/api/projection","auth":"bearer","desc":"Push a projection of a company file. Body: {tenant?, commit?, mode: replace|merge, nodes: [{path, content}]}. Replace mode deletes ONLY the pushing tenant's rows.","returns":"{ok, tenant, built_at, ingested, total, findings[], vectors{embedded_nodes, chunks, errors[]}}. findings reports frontmatter lines the profile does not accept rather than dropping them."},{"method":"GET","path":"/api/nodes","auth":"bearer","desc":"List projected nodes. Query: tenant?, kind?, limit?. Includes projection freshness and kind counts.","returns":"{tenant, projection{built_at, commit_sha, node_count, source}, kinds[{kind, n}], nodes[]}. Default limit 200, maximum 500, clamped silently."},{"method":"GET","path":"/api/runs","auth":"bearer","desc":"Open and recent runs. Query: tenant?.","returns":"{open[], recent[]}. Open runs carry a derived stale flag. Capped at 40 open and 12 recent, with no limit parameter and no paging."},{"method":"POST","path":"/api/runs","auth":"bearer","desc":"Open a run. Body: {tenant?, task, actor_label, harness?, process?, doing?, scopes?}.","returns":"{ok, run_id, started_at}. Carry the run_id through heartbeat, events and finish."},{"method":"POST","path":"/api/runs/:id/heartbeat","auth":"bearer","desc":"Report a run alive; update doing/scopes.","returns":"{ok, run_id, heartbeat_at}, or 404 {error} when no open run has that id."},{"method":"POST","path":"/api/runs/:id/finish","auth":"bearer","desc":"Close a run with a terminal status.","returns":"{ok, run_id, status} with the terminal status that was recorded."},{"method":"GET","path":"/api/events","auth":"bearer","desc":"Recent events. Query: tenant?, limit?.","returns":"{tenant, events[]}, newest first. Default limit 50, maximum 200, clamped silently."},{"method":"POST","path":"/api/events","auth":"bearer","desc":"Record an operational event. Body: {tenant?, type, title, detail?, amount?, needs_you?, actor?, run_id?}.","returns":"{ok, ts, type} with the timestamp the event was recorded at."},{"method":"POST","path":"/api/asks","auth":"bearer","desc":"Create a decision link. Body: {tenant?, ask, becomes, because?, cost?, diff?, branch?, base_sha?, run_id?, pr_number?, repo?}.","returns":"{ok, key, url}. The URL is the decision page, and holding it is what confers the right to answer."},{"method":"GET","path":"/api/asks","auth":"bearer","desc":"Rulings given that nothing has carried out yet (the lander's poll).","returns":"{asks[]}: rulings given but not yet carried out. This is the lander's poll, a lander being a machine that holds your git credential."},{"method":"GET","path":"/api/asks/:key","auth":"bearer","desc":"One ask's status and ruling, what the founder answered.","returns":"{key, status, ruling, ruled_at, ask, branch}. ruling is the answer in the founder's own words."},{"method":"POST","path":"/api/asks/:key/done","auth":"bearer","desc":"Mark a ruling carried out (the lander merged/closed the PR).","returns":"{ok}. Marks a ruling carried out so the poll stops returning it."},{"method":"GET","path":"/api/members","auth":"bearer","desc":"List members and invite codes (the Founder's own surface).","returns":"{members[]} with tenant, slug, name, role, status and invite code."},{"method":"POST","path":"/api/members","auth":"bearer","desc":"Invite a member. Body: {tenant?, name, role: founder|steward|observer, charter?}.","returns":"{ok, tenant, slug, name, role, invite_code}. The invite code is shown once, here."},{"method":"POST","path":"/api/members/:slug/revoke","auth":"bearer","desc":"Revoke a member. Stops new authorizations; never touches the repository.","returns":"{ok, slug}. Stops new authorizations. It never touches the repository."},{"method":"GET","path":"/d/:key","auth":"link","desc":"The decision page: one question, evidence attached, yes or no. Holding the link is holding the right to rule.","returns":"An HTML page rather than JSON: one question, the evidence, and two buttons."},{"method":"GET","path":"/mcp","auth":"oauth","desc":"The MCP mount (streamable HTTP). OAuth via personal invite code; the founder's machine bearer also works.","returns":"The MCP streamable HTTP transport. Unauthenticated requests answer 401 with a WWW-Authenticate header pointing at the OAuth metadata."}]}