lattice
Enables coding agents to query the dependency graph of a GitHub backlog, retrieve ordered ready work and prerequisite chains, atomically claim issues, and report blockers—so agents work in the correct order and safely in parallel.
README
Lattice
A backlog is a flat list pretending to be a plan.
Lattice infers the dependency graph hidden in your GitHub issues and serves the resulting schedule to coding agents over MCP — so one expensive reasoning pass becomes the scheduler for every cheap agent run after it.
It runs on its own. Issue events and a schedule trigger it; nobody clicks anything.
It never writes to GitHub. Issues are a data source, not a data store.
Microsoft Hackathon 2026 · Challenge: Collaboration using GitHub Planning & Tracking Tools in the Agentic Age
The problem
Issues depend on each other. The API must exist before the UI consumes it; the schema migration before the query; the type contract before the four things that import it. Humans hold that ordering in their heads and it never gets written down.
That was tolerable when a human picked the next ticket. It stops being tolerable when your teammate is an agent:
- An agent handed a flat backlog does the wrong work in the wrong order — it writes a frontend against an endpoint that doesn't exist yet, and the output is garbage no matter how good the model is.
- Every agent run re-derives the same ordering from scratch. That triage pass is the expensive part of the request, repeated N times and thrown away each time.
- Nothing says what is safe to run in parallel — which is the entire reason to have more than one agent.
GitHub already has the data model for this, and it is empty. Issue dependencies (blocked_by / blocking) went GA in 2025 with full REST, GraphQL and gh support. Almost nobody fills them in, because doing so is manual and pairwise — O(n²) human effort for a payoff no single person feels. And where they are filled in, GitHub renders them as a flat text list; there is still no graph view.
So: the schema exists, the data doesn't, and the view doesn't.
What Lattice does
GitHub (read only)
issues · blocked_by · sub-issues
│
▼
┌──────────────────────────────────────┐
│ BACKEND │
│ inference ──► the full graph │
│ │ │
│ REST API ◄────┴────► MCP │
└──────────┬─────────────────┬─────────┘
│ REST │ MCP
▼ ▼
interactive graph coding agents
(human: what's next) (agent: what's next,
what's parallel, claim)
The one architectural commitment: GitHub is a data source, not a data store. Lattice reads issues, native blocked_by and sub-issue hierarchy every run, and writes nothing back — no dependencies, no comments, no labels.
That makes the system non-destructive by construction, which is what earns it the right to run unsupervised. There is no automatic writer that could corrupt a shared repo and no pruning logic that could delete a dependency someone recorded by hand. The worst a bad inference can do is mis-order our own suggestions until the next run corrects it.
The write path runs the other way: humans write, Lattice reads. Anyone who wants to overrule the graph edits blocked_by on GitHub, and the next run treats it as ground truth the model may not contradict.
Why this answers the challenge
The hackathon asks: "what does good collaboration look like when part of your team isn't human?"
Coordination between human and non-human teammates is scheduling — and a scheduler that needs a human to approve each decision isn't a scheduler, it's a queue with extra steps.
So Lattice maintains the ordering by itself, continuously, and both kinds of teammate read from the same graph. Agents don't just consume it: an agent that hits an unrecorded blocker reports it back, and the graph is more accurate for whoever asks next.
The shared workspace gets better as anyone works in it. Humans stay in control by correcting it — pinning an edge, suppressing one, or just editing blocked_by on GitHub, which the next run picks up as ground truth — rather than by standing in front of it.
Status
Working end to end. Analysed against its own 54-issue backlog: one model request produced 63 candidate edges, 40 survived validation, 16 became blocking, across 3 waves.
Built: the pipeline, the store, the REST API, the MCP server, the interactive graph, and the agent loop.
Not built yet: the scheduled GitHub Action, DEMO_MODE fixtures, deployment.
Quickstart
No database and no GitHub token needed to look around: the store runs on
PGlite (real Postgres, embedded), and gh auth token is
used automatically if GITHUB_TOKEN is unset.
npm install
npm run build # types + backend (tsc) + web (next build)
npm test # 11 graph unit tests, then writes artifacts/graph.json
Run it
Two terminals. The backend runs compiled — that path has no native binaries in it and does not break:
# terminal 1
npm run build && npm start -w @lattice/backend # :3001
# terminal 2
npm run dev -w @lattice/web # :3000
Open http://localhost:3000, paste any public GitHub repo URL, and it analyses it. Repos you have already analysed are listed on the same page.
npm run dev(both services with hot reload) usestsx. If it fails withThe package "@esbuild/darwin-arm64" could not be found, npm has dropped an optional binary —rm -rf node_modules package-lock.json && npm install --include=optionalfixes it. The compiled path above avoids this entirely.
Analyse a repo
Needs an OpenRouter key. Copy .env.example to .env and set
OPENROUTER_API_KEY, LATTICE_OWNER, LATTICE_REPO.
npm run analyze # ~1 model request for a 50-issue backlog
Expect this to take a few minutes: Ox Alpha is a reasoning model and the whole backlog goes in one call. Re-runs are instant — responses are cached by prompt hash, which is also what protects the 50-request/day free-tier quota.
| Command | What it does |
|---|---|
npm run build |
Builds all three packages |
npm test |
Graph unit tests, then emits artifacts/graph.json + schedule.json |
npm run analyze |
One pipeline run against LATTICE_OWNER/LATTICE_REPO |
npm run agent -- --agents 3 |
Three agents claim work over MCP; asserts leases are atomic |
npm start -w @lattice/backend |
Compiled backend on :3001 |
npm run dev -w @lattice/web |
Web app on :3000 |
Set DATABASE_URL to use hosted Postgres (Neon) instead of the embedded one.
Stop the backend with Ctrl-C, not kill -9
PGlite writes to a real Postgres data directory. A hard kill mid-write corrupts
it and the store is gone. The server closes the database on SIGINT/SIGTERM, so
Ctrl-C is safe. If it does get corrupted, npm run import -w @lattice/backend
restores the last graph from artifacts/graph.json — no model request, no
GitHub token.
Check it without a browser
curl localhost:3001/api/health
curl "localhost:3001/api/graph" | jq '.stats'
curl -X POST localhost:3001/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Using the MCP server
The backend serves MCP over Streamable HTTP at /mcp, alongside the REST API.
Start the backend and it is live — no separate process.
npm run build && npm start -w @lattice/backend # :3001, MCP at /mcp
Claude Code
claude mcp add --scope local --transport http lattice http://localhost:3001/mcp
claude mcp list # lattice: ... - ✔ Connected
A committed .mcp.json in this repo does the same thing for anyone who clones
it, so claude picks the server up automatically.
Copilot coding agent
Copilot's cloud agent needs a public URL, so deploy the backend first (issue
#52). Full walkthrough, including how to point a different repository at a
Lattice instance: docs/13-using-lattice-mcp.md.
Auth is skipped entirely when COPILOT_MCP_LATTICE_TOKEN is unset, which is
what makes local use zero-config; set it before exposing the backend.
Any other client
npx @modelcontextprotocol/inspector # then connect to http://localhost:3001/mcp
The two directions
Top-down — "what should I work on?"
list_ready_work issues nothing is blocking, ranked by how much they unblock
claim_next_issue take one atomically, with a briefing; two agents never collide
report_progress returns what your work just unblocked
Bottom-up — "I want to ship #6, what has to exist first?"
plan_for_issue the whole prerequisite chain, in build order
get_issue_context blockers, dependents, and what they need from you
explain_dependency why an edge exists, with the quote it was inferred from
report_dependency a blocker you discovered; enters the graph for everyone
Point an agent at a target and it gets the ordered plan:
plan_for_issue(6)
2 issue(s) must land before #6, in 2 step(s).
step 1: #3 Put Adyen behind a PaymentProvider interface
step 2: #4 Checkout session endpoint returns a provider-agnostic session
then #6 Native checkout in the app
Everything inside a step is independent, so it can be done in any order or handed to several agents at once. The issue panel in the web app shows the same plan with a Copy button that hands it to an agent as a prompt.
The machine-readable graph
npm test writes artifacts/graph.json and artifacts/schedule.json. That
makes the schedule diffable: change the cycle-breaking weights and the
critical-path shift shows up as a reviewable diff rather than a vague feeling
that the graph looks different. It is also the cheapest integration test in the
repo — if that file is well-formed and acyclic, the whole pure core is wired up.
Architecture at a glance
Two services in one npm-workspaces monorepo:
apps/backend— reads GitHub, runs inference, owns the store, serves the REST API and the MCP server.apps/web— the interactive graph. Holds the backend URL and an API token and nothing else: no database URL, no GitHub token, no model key.
Documentation
| Doc | What's in it |
|---|---|
docs/00-context.md |
Hackathon context, judging criteria, submission requirements |
docs/01-architecture.md |
Components, data flow, where state lives, stack decision |
docs/02-inference-pipeline.md |
The five inference layers, the LLM prompt, anti-hallucination guards |
docs/03-graph-scheduling.md |
Tarjan, cycle breaking, waves, critical path, blast radius |
docs/04-mcp-surface.md |
The seven MCP tools agents call |
docs/06-workstreams.md |
The five-way parallel split for the team |
docs/07-demo-script.md |
The two-minute demo, beat by beat |
docs/08-risks.md |
Honest weaknesses, fallbacks, stop-loss rules |
docs/09-github-api-notes.md |
Verified endpoints, headers, and the gotchas that will bite |
docs/10-model-provider.md |
OpenRouter + Ox Alpha: setup, schema caveat, rate limits, privacy |
docs/11-graph-store.md |
Where the graph is persisted, and the three cache layers |
docs/12-rest-api.md |
The REST contract the web app consumes |
docs/13-using-lattice-mcp.md |
Connect Lattice to another GitHub Copilot App repository |
AGENTS.md |
How agents should work in this repo |
Quickstart
Not yet — the scaffold is issue #1. This section is the "can someone else run it from your README?" judging criterion, so it gets written properly before submission. Target: clone to graph in ≤5 commands, plus a
DEMO_MODE=1fixture path that needs no tokens at all.
License
MIT
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.