Make Tapper remote-only - #89
Merged
Merged
Conversation
Tapper carried two storage worlds. A local keg resolved to a filesystem repository on disk; a remote keg resolved to the Hub's operation API. The two paths diverged constantly -- locking, snapshots, dex maintenance, and schema validation each had to be implemented and tested twice -- and the local path was the one that could not enforce authority, because there is no ACL on a directory. Settle on the Hub. `keg.Keg` now has exactly one production shape: `RemoteKeg` for clients, `LocalKeg` over the Hub's server-side `PgRepo` for the Hub itself. The filesystem repository and its lock, snapshot, and event backends are gone, along with the atomic-write and operation scaffolding that existed only to serve them. Tests move to a purpose-built memory repository under internal/testkegrepo, which is test-only by construction and cannot be reached from a released binary. With one backend, several surfaces collapse: - The pruned `keg` binary loses its reason to exist -- it existed to offer project-local resolution -- so `tap` becomes the single entrypoint and the goreleaser build, Homebrew formula, and install task go with it. - Configuration and flight manifests become Hub-resolved documents rather than files discovered on disk. - The graph visualization surface is removed: the Bun/TypeScript frontend, its embedded bundle, and the Keg and Tap graph operations. Dex links, backlinks, and RelatedNodes remain the supported relationship surface. - `config` is renamed to `settings` throughout, matching what the document has always been. The same consolidation makes authority enforceable, so this commit also lands the behavior that depends on it: - Protected writes require read-derived precondition tokens. Settings, schemas, node updates, moves, and removals now carry hashes with actionable conflict recovery instead of last-writer-wins. - Launch roots are immutable and pinned per connection, with per-call flight selection across the pinned root's recursive accessible graph. A subflight is an ordered list entry on its parent, not an assertion about the whole graph, so cycles are tolerated by deduplication rather than rejected by a write-time walk. - Keg settings and schema writes require admin. They steer every agent on the keg, so editor is not sufficient. - `tap launch` no longer requires a configured flight. Bootstrapping a first flight needs an agent session, and that session needed a flight that did not exist yet; a flightless launch runs under identity authority and warns which access is in play. - JSON schemas are embedded and materialized so editors can drive completion and validation against the running build.
Bring the prose in line with the single-backend architecture: `tap` is the only CLI, kegs resolve through configured hubs rather than disk discovery, `LocalKeg` runs over the Hub's PgRepo, and the keg document is called settings. Replaces the keg-config configuration page with keg-settings and drops the graph visualization references. CHANGELOG.md keeps its hand-written Unreleased notes for now; the release workflow regenerates the file from Conventional Commit subjects, so this prose is reference material rather than the published notes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tapper carried two storage worlds. A local keg resolved to a filesystem repository on disk; a remote keg resolved to the Hub's operation API. The two paths diverged constantly — locking, snapshots, dex maintenance, and schema validation each had to be implemented and tested twice — and the local path was the one that could not enforce authority, because there is no ACL on a directory.
This settles on the Hub.
keg.Kegnow has exactly one production shape:RemoteKegfor clients,LocalKegover the Hub's server-sidePgRepofor the Hub itself.What changes
internal/testkegrepo, which is test-only by construction and cannot be reached from a released binary.kegbinary is retired. It existed to offer project-local resolution, sotapbecomes the single entrypoint; the GoReleaser build, Homebrew formula, and install task go with it.RelatedNodesremain the supported relationship surface.configis renamed tosettingsthroughout, matching what the document has always been.Consolidating onto one backend is what makes authority enforceable, so the behavior that depends on it lands here too:
tap launchno longer requires a configured flight. Bootstrapping a first flight needs an agent session, and that session needed a flight that did not exist yet; a flightless launch runs under identity authority and warns which access is in play.Verification
Against a clean worktree at
6b911eb:go build ./...,go vet ./...— passgo test ./...— pass acrossinternal/apidoc,pkg/cli,pkg/integrations,pkg/integrations/adapters,pkg/keg,pkg/mcp,pkg/parity,pkg/schemas,pkg/tappergo test -race ./pkg/keg ./pkg/tapper— pass (matches CI)task lint:docs— passtask render-integrationsis idempotent; regenerating produces no further diffThe implementation commit was verified in an isolated worktree before the documentation commit was added, so it stands on its own.
Coordination — do not merge alone
This is one half of a coordinated change. Tapper Hub is the server side of the same contract and must land in lockstep; merging this first leaves the Hub pinned to a Tapper that no longer matches its code. Delivery is gated until both sides are jointly verified and the Hub's dependency pin is updated as part of that.
Review notes
The history is deliberately two commits rather than a narrative sequence.
pkg/keg/keg_iface.go,pkg/keg/keg_aggregate.go,pkg/tapper/tap.go, andpkg/cli/cmd_root.goare each touched by every theme in the change, so any thematic slice that needs one of them pulls in the others' edits and cannot compile until its dependencies land.integrations/contentandintegrations/renderedare both embedded viaintegrations/embed.go, making them build inputs that belong with the implementation. The result is that every commit here builds and tests clean.