TL;DR

I'm extracting Product Spine from Ailised: a reusable core for defining product operations and connecting them to web interfaces, native clients, command-line tools, and agents. The kernel checks and Swift client are in place. The complete path through adapters, persistence, and a live interface is still being extracted.

The same action should mean the same thing

A person clicks a button in a browser. An operator runs a command. An agent calls a tool. An iPhone app sends a request.

If all four are creating the same thing, I want them to follow the same product rules. They should agree about what the action accepts, whose workspace it affects, and whether the caller is allowed to do it.

In Agentic-first Should Not Mean Interface-less, I wrote about giving people an understandable interface to an agentic product. Spine is the implementation work behind that direction: a shared core that those interfaces can call.

It comes out of Ailised, where a dashboard, HTTP API, CLI, and MCP interface connect to shared product operations. I'm separating that structure from the particular products running inside it so I can reuse it elsewhere.

Start with an operation

The unit in Spine is an operation: create a task, list records, update a setting. Each operation has an identity and a declaration of how it appears through the supported interfaces.

The catalog records its domain, action, whether it changes state, and transport details such as an HTTP route, CLI command, or MCP tool name. Related contracts describe responses, permissions, and scope.

For a task application, the intended flow looks like this:

  1. A web app, phone, command, or agent requests task.create.
  2. The shared product function checks permission for that operation and scope.
  3. A mutation workflow validates the request and persists the change through a storage interface.
  4. A revision signal tells open clients to fetch the updated state.

That is the reference path I want to prove in Spine. The database-backed Tasks example is still unfinished.

The catalog alone cannot perform the action. There still needs to be a real implementation behind it. Keeping the declaration and the implementation connected is a large part of the work.

Catch missing pieces before serving requests

An operation can look finished in one interface while being incomplete in another. A tool declaration might exist without a handler. A plugin might claim an operation that another part of the product already owns.

Spine has checks for ownership, capability coverage, scope coverage, and agent tool handlers. Its boot function runs assertions supplied by the host, so a failing assertion can stop startup before the application begins serving requests.

There is an important implementation detail here: the host has to wire those checks in. The current boot function coordinates them; it does not discover and secure an arbitrary application automatically. Plugin documentation checks are optional in that function today.

For agent calls, the shared tool executor resolves the operation, checks that a handler exists, authorizes the request, and then invokes the handler. I want that path to remain easy to inspect as the extraction grows.

Keep product features outside the core

Ailised contains actual product capabilities. Spine's kernel should remain usable without knowing about those domains.

The kernel holds operations, authorization, scope, and conformance checks. Product features belong in the host or its plugins. Storage sits behind interfaces, so the core does not need to import a database library.

Convex is the planned default durable adapter. Other stores would need their own implementations of the required interfaces; they are not supplied integrations yet.

This boundary also applies to the interface. The reusable core should not require a particular dashboard design or a React application just to execute an operation.

Bring the phone along

The native client is one of the concrete pieces already in the repository.

The iOS generator exports Swift operation identifiers and JSON contracts from the catalog. The Swift package provides an HTTP client and schema validation for iOS and macOS. A screen can invoke a declared operation through that client.

There is also a live-update helper: a remote revision triggers an HTTP refetch, while a locally applied snapshot can skip the next revision. The signal carries the fact that something changed; the client fetches the product data through the API.

The broader persistence and live-update integration still needs to be completed and proved across the reference application.

The proof I want next

The next useful milestone is a small Tasks application backed by a real database. Creating a task through HTTP should make it visible through the CLI. Updating it through MCP should change the same record. An open interface should update without a manual refresh.

Permission denial should be consistent across interfaces. Deliberately removing a guard or handler should stop the application from booting.

Those are the extraction's acceptance criteria, rather than results I'm claiming are complete today. The portable kernel, workflow and plugin contracts, and Swift client give me a starting point. Adapters, the React shell, the full Tasks path, and integration back into Ailised remain work ahead.

What I want from Spine is straightforward: when I add a product operation, I want people and agents to reach the same behaviour through whichever interface makes sense for them.