Skip to content

v0.4.0 — Ambit

Choose a tag to compare

@zz-plant zz-plant released this 13 Aug 15:18
· 168 commits to main since this release

Capability Graph is now Ambit.

Ambit — the range of someone's authority or action.

The old name described the data structure. The new one describes the subject: what you, your agents, and your machines can jointly do, and which cheap changes would let you do more.

This release is mostly repair. The previous version documented a tool that did not run — so before anything new could be built on it, the things it already claimed had to become true.


The tech tree became a tech tree

The view was called ERAS and grouped by domain. Every node carried an era field and nothing used it, so there was no progression to read.

Eras now drive the columns, oldest on the left, and three states carry the meaning:

State Appearance Meaning
Reached filled, full colour something in your config provides this, and the graph records what proved it
Researchable dashed halo + time estimate prerequisites met, nothing configured yet — the frontier
Locked faded still out of reach

Everything unreached previously shared one flat opacity, which collapsed "you could take this next" into "this is far away" — the single most useful distinction the data supports.

A curated tree of 33 capabilities across 7 eras ships with it, from shell execution up to running the whole loop offline. Like a Civ tech tree it is authored content: everyone gets the same tree and differs only in where they are on it. The local-model path is a full spine — runtime, tool calling that actually works, extended context, local embeddings, offline operation.

The graph has edges

Nothing in the repository had ever written to the dependencies table. Thirteen read sites, zero writers, and no code path created a combo either — so tt combos, near, fork, bottlenecks and impact returned empty permanently.

Seeding now writes the relationships the config states outright:

provider:local-code ──▶ model:local-code/qwen3-coder ──▶ agent:plan

A provider is a hard prerequisite for the models it serves; a model for any agent pinned to it. Read from opencode.json, not guessed.

Security

The server could not parse, so none of this was reachable — but restoring it made it reachable, and it needed to be closed:

  • Cross-origin config writes. CORS reflected any Origin, and /api/config/apply accepted addMcp — an MCP entry carries a command OpenCode executes. That made visiting a web page a path to code execution. Now loopback-only, with non-local origins rejected before routing, because a simple request skips preflight and reaches the handler regardless of response headers.
  • Entry creation removed. The API can toggle an MCP and edit an existing agent's description or model. It cannot create entries. Adding a server generates a snippet you paste yourself.
  • Prototype pollution. Entry lookups used a bare truth test, which accepts __proto__ — inherited from Object.prototype — and the assignment that followed would have polluted every object in the process.

Legibility

39 CSS declarations and 34 inline styles sat below 11px, and --text-muted was at 2.75:1 contrast — under the WCAG AA floor of 4.5 — while carrying most of those labels. There is now a type scale with nothing readable below 12px and an accessible palette.

Three panel metrics did not survive scrutiny and were removed: one plotted description.length as telemetry, one restated the status printed above it, and influence was rank rescaled to a percentage. The diagnostics headline was a rating derived from an invented penalty scale, floored at zero so three errors and thirty looked identical — replaced by the finding counts it came from, which can be checked against the list.

Things that never worked

  • tt could not find its own enginecli.js resolved ROOT one level above the package, so every command failed
  • bootstrap.sh failed on a clean clonebun install -q is an npm flag; set -e aborted before installing anything
  • The tree filter was unclickable — positioned under the sidebar, rendering but permanently hidden
  • The tracking plugin never recorded anything — a foreign key made every write fail, and the error was swallowed
  • OPENCODE_CONFIG was ignored by the engine despite being documented

Performance

Initial JavaScript 1.15 MB → 98 KB. Three.js is 1 MB of that and is now lazy-loaded behind the default view, which is 2D and never needed it.


Install

brew install zz-plant/tap/ambit
git clone https://github.com/zz-plant/ambit.git
cd ambit && ./bootstrap.sh

Then tt on its own — it shows where you are, what is one step away, and what to do next.

Requires Bun for the visualiser and Node 22+ for the engine.

Breaking changes

Demo URL zz-plant.github.io/capability-graph/ambit. Pages does not redirect — old links are dead
Homebrew zz-plant/tap/capability-graphzz-plant/tap/ambit
Repo URL redirects indefinitely; existing clones keep working
toolchain-viz.db no longer tracked in git — ./bootstrap.sh regenerates it

What is not built

tt combos, near and fork return results from the curated tree, but Ambit still models what exists, not what can be acquired. It cannot take a goal, compute the capability gap, and propose routes across it.

ROADMAP.md describes that work: capabilities separated from providers, the human and the hardware in the graph as actors with their own authorities, acquisition recipes with executable verification, goal-to-delta planning, and authority as a first-class edge. It is governed by one principle:

A capability is not something configured. A capability is an action the system has evidence it can perform.

Nothing in the roadmap is implemented. The README describes only what runs, and both files say so.