Repository navigation
Gemba Remote foundations: M1 OSS core + M2 federation + M3 hosted plane + ASDD Phase 1 - #2
Merged
Merged
Conversation
- internal/auth/credstore: XDG-aware multi-server credential store with 0600 file mode and atomic temp+rename writes - internal/cli/login.go: gemba login + whoami subcommands - internal/server/whoami.go: GET /api/whoami behind auth middleware - OpenAPI documents /api/whoami with WhoamiResponse + Unauthorized - Adds golang.org/x/term as direct dep for masked TTY prompt Subject is hardcoded "operator" until per-request subject is threaded through the auth middleware (follow-up). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 12 paths under /api/v1/workspaces/{wsid}/ covering spec/decision/
constitution lint and the no-args status convenience endpoint
- X-GEMBA-Confirm declared once in components.parameters (UUID v4
pattern) and $ref'd from every mutating endpoint
- SSE schema for /watch documented via text/event-stream + description
enumerating bead.status, spec.drift, reconcile.complete event types
- Stub handlers (specs.go, decisions.go, labels.go, status.go) return
501 not_implemented JSON envelope; all auth-gated; nonce-required
on writes via requireConfirmNonce wrapper
- 524 tests pass across 5 server packages
Routes registered in router.go (the actual mounting point) rather
than bootstrap.go as the brief suggested.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- internal/adapter/dolt/supervisor: spawns dolt sql-server on a loopback TCP port; blocks until SELECT 1 succeeds; 5s health probe; exponential backoff (500ms→30s, max 5 consecutive fails) - internal/cli/serve.go: --embedded-dolt (default true unless --dolt-url set) and --dolt-data-dir flags; supervisor starts before HTTP listener; deferred Stop runs after srv.Shutdown so HTTP drains against live dolt - internal/server/readyz.go: /api/readyz returns 503 with checks.dolt:"down" when supervisor is not Ready; 200 with checks.dolt:"ok" when up - 729 tests pass; real-dolt integration test gated on dolt-in-PATH Went TCP (not UNIX socket) because dolt --socket support has been historically inconsistent. Supervisor runs but bd adaptor still resolves Dolt via --dolt-url; wiring is gm-o9t8.1.7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves conflicts in docs/api/openapi.json and
internal/server/openapi/openapi.json by combining /api/whoami
from gm-o9t8.1.1.2 with the 12 /api/v1/workspaces/{wsid}/...
paths from gm-o9t8.14. Both files byte-identical post-merge
(TestOpenAPISpec_DocsCopyMatches gate satisfied).
47 total paths (34 existing + whoami + 12 govern).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- internal/client/gen: oapi-codegen v2.7.0 output covering all 47 endpoints - internal/client/client.go: wrapper injects bearer from credstore, X-GEMBA-Confirm UUID v4 on POST/PATCH/PUT/DELETE, User-Agent - internal/client/errors.go: maps Error + UnauthorizedResponse envelopes to sentinels (ErrUnauthorized/NotFound/Conflict/NotImplemented/ BadRequest) and a generic *ServerError fallback - internal/client/preprocess_spec.go: rewrites OpenAPI 3.1 type-array nullables to 3.0.3 nullable:true since oapi-codegen v2.x doesn't yet ingest 3.1; encapsulated in go generate - gemba whoami rewired to typed client; gemba login stays ad-hoc (chicken-and-egg: no credential exists yet at login time) Adds google/uuid as direct dep; oapi-codegen + runtime via go.mod. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Naming reconciliation: existing gemba bead subverbs (list, show, status,
size, extract, backfill, targets, concepts) moved under gemba bead
enrichment subgroup; top-level gemba bead now owns the CRUD verbs.
- internal/cli/bead_crud.go: create/list/show/update/close/note verbs
via direct JSON requests through internal/client's auth + nonce
pipeline (typed-client regeneration deferred to follow-up)
- internal/cli/bead.go: registers bead_crud verbs alongside the new
enrichment subgroup; existing tests mechanically updated to nest
under enrichment
- internal/client/work_items.go: hand-written wrappers (CreateWorkItem,
ListWorkItems, GetWorkItem, PatchWorkItem, NoteWorkItem) riding the
same RequestEditor pipeline as the generated client
- internal/server/work_items_notes.go: 501 stub for POST notes endpoint
- OpenAPI updated: POST /api/work-items (createWorkItem), PATCH
/api/work-items/{id} (updateWorkItem), POST /api/work-items/{id}/notes
(noteWorkItem); CreateWorkItemRequest, WorkItemPatch, NotImplemented
components added
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds TestSchemaRoundTrip + TestSchemaRoundTrip_RealDolt covering the M1 acceptance criterion. Three table-driven subtests verify create → list → show preserves the CLI-visible bd-bead shape (id, title, status, labels, priority, relationships, created_at, updated_at, kind, state_category). The real-dolt path is skipped with a clear pointer to the wire-up follow-up: this worktree lacks both an in-process dolt sql-server supervisor and CLI bead-CRUD wrappers, so the round-trip is wired through testadaptors.FakeWorkPlane with an in-memory store that mimics what a write-through bd/dolt adaptor would do. Reference shape is anchored on gm-o9t8 drawn from ~/gt/gemba/.beads/issues.jsonl.
…ring
- New `gemba status` subcommand renders the Ad-hoc dashboard template
from gm-o9t8.8 using text/tabwriter for alignment
- Bare `gemba` dispatches via root.RunE → newStatusCmd().RunE
- detectMode(workspaceRoot) is a stub returning "ad-hoc"; other three
modes (Scaffolded/Governed/Reverse-governed) deferred to ASDD epic
gm-v0sp (spec.md + .lockfile.json machinery not yet built)
- Data path probes /api/v1/workspaces/_/status (501 stub today) and
falls back to /api/work-items?status={ready,in_progress,open,closed}
- Flags: --server, --json, --mode
- 7 new tests in status_test.go; all 248 CLI tests pass
cmd/gemba serve now constructs a MemStore-backed tenant.Store and seeds DefaultTenant via Migrate so GET /api/v1/tenants/t-default returns metadata out of the box in single-user mode. Dolt-backed SQLStore + real tenant rows land with the OAuth follow-up (gm-o9t8.3.5). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces internal/egress: Rule data model, MemStore + SQLStore backends, hardcoded Defaults() baseline allow-list with terminal default-deny, and a label-glob host-pattern matcher (* = one label, ** = one-or-more labels). Defaults are never persisted and never deletable; workspace rules with higher priority shadow them via Effective(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds internal/adapter/firecracker with a Supervisor interface mirroring
the dolt supervisor's Start/Stop/Wait shape, factoring VMs as the unit
of lifecycle instead of a single long-lived child.
- Build-tag layout: firecracker.go (shared types) + kvm_{linux,other}.go
+ firecracker_linux.go (real firecracker-go-sdk path) + firecracker_
fallback.go (non-Linux bash -c shim) + fallback_{unix,windows}.go for
process-group / signal helpers.
- KVMAvailable() checks /dev/kvm device-node mode + open(O_RDONLY); off-
Linux it's a const false.
- Linux path uses firecracker-go-sdk v1.0.0: Config{KernelImagePath,
Drives, MachineCfg{Vcpu, Mem}}, MMDS for secret injection via
machine.SetMetadata, tap-per-VM naming on the gemba0 bridge with dev-
mode network skip (full bring-up belongs to gm-o9t8.3.6.*).
- Fallback spawns bash -c (default exec cat) with Spec.Secrets injected
as env, single-shot Wait goroutine for zombie-free reaping, polite
SIGTERM via process-group then ctx-cancel SIGKILL on timeout.
- Tests run on macOS/CI: lifecycle (Start/Stop/idempotent-Stop/Wait),
secret env injection (subprocess self-asserts), and concurrent-VM ID
uniqueness + independent teardown. Real-Firecracker test placeholder
gates behind requireKVM and currently t.Skip's pending gm-o9t8.3.2.2
kernel+rootfs artifacts.
Builds clean on macOS (fallback path) and cross-compiles with
GOOS=linux -tags linux. go test ./internal/adapter/firecracker/...
passes (4 tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add server-side scaffolding for GitHub OAuth: - ServeConfig gains OAuthGitHubClientID, OAuthGitHubClientSecret, MultiTenantMode. Flags --oauth-github-client-id / --oauth-github-client-secret / --multi-tenant; env vars GEMBA_OAUTH_GITHUB_CLIENT_ID / GEMBA_OAUTH_GITHUB_CLIENT_SECRET / GEMBA_MULTI_TENANT. OAuthEnabled() derived from both being set. - internal/auth/oauth: new package with Config / OAuth / CallbackPath. New() validates the config matrix (empty = ErrUnconfigured; partial = ErrPartialConfig). DisabledHandler returns 503 with stable error.code "oauth_not_configured" so callers distinguish "server doesn't speak OAuth" from auth errors. - /api/v1/auth/oauth/github/callback registered on the router. 503 when OAuth is unconfigured, otherwise serves the device-flow status placeholder. - ValidateOAuthForMultiTenant runs at boot and refuses to start a multi-tenant rig without OAuth. - Token-storage shape: internal/server/oauth_tokenstore.go. auth_tokens (tenant_id, github_id, device_label, hash_phc, created_at) with PRIMARY KEY (tenant_id, device_label). MemTokenStore for tests + single-user fallback; SQLTokenStore for the Dolt path. argon2id PHC strings via internal/auth.HashToken; plaintext is never persisted. Tests: - internal/auth/oauth/oauth_test.go: TestNew_ConfigMatrix, TestCallbackURL, TestDisabledHandler, TestFetchUserAt, TestFetchUserAt_Rejected. - internal/config/bind_test.go: TestOAuthConfigMatrix. go build ./... + go test ./internal/config/... ./internal/auth/oauth/... pass. Backwards compat: single-user PAT path untouched. OAuth is opt-in; unconfigured = silent 503 on the OAuth routes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds /api/v1/workspaces/{wsid}/egress-rules (GET/POST), .../{id}
(DELETE), and .../effective (GET) to the tenant-aware route group.
POST/DELETE are nonce-gated; defaults are immutable through the
surface. Wires an optional EgressAuditor that emits the new
audit.EventEgressRuleCreate / .Delete events declared in
internal/server/audit/types.go. OpenAPI documents all four paths;
docs/api/openapi.json is regenerated from the embed source.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the write half of the tenant admin surface: POST/PATCH/DELETE on /api/v1/tenants plus a paginated GET listing. Mutations are nonce-gated through the shared X-GEMBA-Confirm cache; admin gating is the v1 stub (any valid bearer == admin) until gm-o9t8.3.5.3 lands OAuth-bound roles. - Extend tenant.Store with Update / Delete / paginated List(opts) and add ErrTenantExists for create-collision distinguishability. - Add audit.Auditor (Append + Query) with FileAuditor + MemAuditor implementations and tenant.create/update/delete event kinds so admin handlers emit a queryable audit trail. - Router wires AttachAuditor + AttachTenantWorkspaceCount hooks; the delete handler refuses orphaning workspaces unless ?force=1, and t-default is permanently undeletable. - OpenAPI: add four new paths, hoist tenant schema into components.
CLI side: - internal/cli/login.go: add --github + --github-client-id flags. When --github is passed, route to runGitHubLogin instead of the PAT path. Client id resolution: --github-client-id > GEMBA_OAUTH_GITHUB_CLIENT_ID. - internal/cli/login_github.go: three-phase implementation. 1. requestDeviceCode: POST https://github.com/login/device/code with client_id + scope=repo,user:email. Print the user code + the verification URL (defaults to https://github.com/login/device). 2. pollForGitHubToken: POST https://github.com/login/oauth/access_token every <interval> seconds. authorization_pending = keep polling; slow_down = bump the interval by +5s; ctx cancel = clean exit. 3. exchangeGitHubTokenWithGemba: POST gemba's /api/v1/auth/oauth/github/login with the GitHub access token; receive the gemba bearer + tenant id; persist via credstore. Server side: - POST /api/v1/auth/oauth/github/login mounted outside the apiAuth block (the CLI hits it BEFORE it has a gemba bearer). Handler: - validates the GitHub token via GET https://api.github.com/user (test seam: SetGitHubUserFetcherForTest). - provisions a tenant on first login (tenant.NewID + Create); on subsequent logins resolves the existing row by github_id. - mints a 32-byte hex bearer (crypto/rand), hashes with argon2id via auth.HashToken, stores (tenant_id, github_id, "github-<sha>", hash_phc) via TokenStore.Put. Plaintext is never persisted. - emits best-effort auth.login audit event via AttachAuditEmit hook. - internal/server/oauth_tokenstore.go: MemTokenStore + SQLTokenStore with PRIMARY KEY (tenant_id, device_label) upsert. VerifyToken is O(n) argon2id over the stored rows (n is small for v1). - internal/tenant/store.go Migrate now also runs CREATE TABLE IF NOT EXISTS auth_tokens, so a single tenant.Migrate call provisions everything the multi-tenant control plane needs. Tests: - internal/cli/login_github_test.go: TestRunGitHubLogin_HappyPath, TestPollForGitHubToken_AuthorizationPending, TestPollForGitHubToken_SlowDown, TestPollForGitHubToken_Cancel, TestExchangeGitHubTokenWithGemba_FirstLogin, TestExchangeGitHubTokenWithGemba_Existing. - internal/server/oauth_test.go: TestOAuthLogin_FirstLogin (creates tenant + verifies argon2id hash); TestOAuthLogin_SubsequentLogin (second login finds existing tenant; no duplicate rows); TestOAuthLogin_Disabled (503 oauth_not_configured envelope); TestOAuthCallback_Disabled / TestOAuthCallback_Enabled. go test ./internal/auth/oauth/... ./internal/cli/... ./internal/server/... ./internal/tenant/... ./internal/config/... passes (1125 tests). Backwards compat: --github is opt-in. The single-user PAT path (login + whoami) is unchanged; legacy callers see no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolved router.go conflict by keeping both feature-set additions (egress store fields + audit/tenantWorkspaceCount fields).
Resolved router.go conflicts by keeping both tenant-CRUD admin routes (from gm-o9t8.3.1.1) and OAuth callback route (from this branch). Fixed test call site for the extended Store.List signature.
Implements VerifyImage/LoadManifest/DefaultImagePaths and a manifest schema describing the kernel + rootfs artifacts. Plain Go so it builds on every host; the Linux supervisor calls VerifyImage before boot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire VolumeMount through Spec.validate, firecracker_linux drives, and the non-Linux fallback (symlink-staged into per-VM tmpdir with env hints GEMBA_VOL_<N>_PATH/_GUEST/_RO). Adds volume_test.go covering validation rejections and subprocess readability.
Adds the build-pipeline scaffolding for the Firecracker microVM image: - vm-image/buildroot/: Buildroot 2024.x external tree with the gemba_defconfig (musl busybox + git + ca-certs + virtio-fs) and a gemba-vm-init package that cross-compiles the Go init binary. - vm-image/init/: standalone Go module (its own go.mod, build-tagged linux+init) that reads MMDS, mounts /workspace, execs the agent, posts a run summary, and powers off cleanly. - vm-image/kernel/: Linux 6.1 microvm-tuned .config fragment plus a README pointing at upstream kernel.org. - vm-image/scripts/build.sh: orchestrator that bakes vmlinux + rootfs.ext4 and writes dist/vm/manifest.json (+ optional ed25519 manifest.sig when GEMBA_RELEASE_KEY is set). - Makefile: vm-image / vm-image-clean targets. On non-Linux hosts the target prints a friendly skip and exits 0. - docs/server/vm-image.md: full lifecycle docs — build, ship, install, verify, fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend TokenStore interface and SQL schema with an opaque token id PK, revoked_at, and last_used_at columns. Add three new handlers under /api/v1/auth/tokens (list, revoke, rotate) plus EventAuthTokenRevoke / EventAuthTokenRotate audit events. Rotate runs inside a SQL transaction so revoke + insert are atomic against the (tenant, device_label) UNIQUE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces the Ruleset/Statement IR that the Firecracker supervisor translates egress.Rule into before handing off to the platform- specific applier. Lives in the firecracker package so both Linux nftables emission and the non-Linux no-op share one source of truth. Hostname resolution is one-shot at apply time via net.DefaultResolver with a 5s timeout; wildcard patterns produce a "no eager DNS" note so the applier can emit a log-only stub instead of blackholing traffic. Translation matrix test exercises five rules with overlapping priorities — accept/drop/log shapes, wildcard fallback, sanitised table names, and stable JSON rendering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add VaultInjector + Options.Vault on the firecracker Supervisor. Both Linux (MMDS payload) and fallback (subprocess env) paths now pull vault.Inject(wsid) at Start, merge with explicit Spec.Secrets (explicit wins), and proceed. Nil vault is a normal path — only explicit secrets are used. NewSupervisor(log) routes through the new NewSupervisorWithOptions for backwards compat. Note: the same Options struct also carries the LifecycleAuditor seam used by gm-o9t8.3.2.7; the two beads share the construction surface so wiring up the supervisor is a single call site.
- egress_linux.go installs a per-VM `gemba_<vm>` inet table with a forward chain hooked at priority 0 and a default-drop policy. Each Statement becomes one nft rule matching iifname == tap + the resolved daddr set; deny rules embed a log prefix `gemba-egress-drop:<rule-id>` for audit ingest. - teardownEgressRules deletes the per-VM table; missing tables are a no-op so the cleanup path is idempotent. - egress_other.go is the no-op stub: returns the translated Ruleset but emits a WARN that egress is not enforced on this platform. - Pull in github.com/google/nftables v0.3.0 + golang.org/x/sys/unix. Linux integration test is gated on `runtime.GOOS=="linux" && euid==0` and skips on every developer host + CI runner we have today. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add internal/dispatch with a Runtime strategy interface, native and firecracker implementations, and SelectRuntime helper. RuntimeNative delegates to a NativeDispatchFunc (preserves the existing tmux dispatch path verbatim); RuntimeFirecracker boots a per-bead microVM via firecracker.Supervisor, mounts workspacelayout.Paths.Repo at /workspace, and returns vm.ID as the run id. Default selection remains native — runtime=firecracker is opt-in via constitution key or --runtime CLI flag.
Adds EgressProvider (narrow Effective() seam) + EgressAware (a type- assertion-only interface implemented by both backends). Callers pass in any source that satisfies egress.Store-shaped Effective lookups. - Linux supervisor: after machine.Start returns, fetches the workspace's effective rules and installs them via applyEgressRules. An apply failure tears down the VM (StopVMM + ctx cancel) and surfaces the error from Start — no VM ever runs without working egress controls. Stop reads VM.egressTable and best-effort deletes the per-VM nftables table. - Fallback supervisor: source-compatible. Translates the plan so dispatch / CLI tooling can introspect it but doesn't touch the kernel; the WARN log in egress_other.go is the visible artifact. - VM gains an unexported egressTable field stamped by the applier. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Declare EventVMSpawn, EventVMDestroy, EventVMEgressDenied audit
constants plus KindVMLifecycle. Add VMLifecycleAdapter that bridges
the firecracker supervisor's narrow LifecycleAuditor surface onto
audit.Auditor — keeps the supervisor free of an audit package import
cycle. Supervisor (both fallback and Linux) emits vm.spawn on Start
success with {vm_id, wsid, kernel_path, mem_mb, cpus}, and vm.destroy
on Stop with {vm_id, duration_ms, exit_status}. EventVMEgressDenied
is declared for gm-o9t8.3.6.2 to use.
…(dispatch) + 3.2.7 (audit events) Resolved conflicts in firecracker_fallback.go / firecracker_linux.go by keeping BOTH the EgressProvider plumbing (from gm-o9t8.3.6.2) AND the VaultInjector + LifecycleAuditor wiring (from this bundle). audit/types.go de-duplicates the EventVMEgressDenied constant that both branches added.
Five interlocked beads landing the ASDD interception surface:
- gm-v0sp.15: gemba spec new --kit scaffolds .claude/commands/{tasks,implement}.md
- gm-v0sp.16: PreToolUse hook handlers reject writes to tasks.md / todo.md
- gm-v0sp.17: constitution schema + parser + 'gemba constitution lint --strict'
- gm-v0sp.18: 'gemba spec preflight' refuses /implement when tasks.md exists
- gm-v0sp.19: ASDD stanza appended to CLAUDE.md via 'gemba spec init --asdd'
Analyzer (gm-v0sp.21) seam preserved: rejected payloads available on stdin
and via GEMBA_REJECTED_PAYLOAD env hand-off.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Foundational implementation of the Gemba Remote epic (
gm-o9t8) and Phase 1 of ASDD (gm-v0sp). 38 commits across four milestones plus the Spec Kit interception layer.gm-o9t8.1gm-o9t8.2gm-o9t8.3gm-v0sp.15–.19Highlights
M1 — Single-user OSS core
gemba login/whoami+ XDG credstore (0600 perms)gemba beadCRUD (existing enrichment verbs nested undergemba bead enrichment)gemba run/gemba agent dispatch/gemba logs -fwith SSE consumergemba diffstreaming git diff from servergemba statusmode-aware dashboardgemba config showwith--server/GEMBA_SERVER/ config.toml / default precedenceweb/dist/.gitkeepcommitted so fresh-checkoutgo build ./...works zero-configM2 — Federation and data ownership
git clone $GIT && dolt clone $DOLTHUB && gemba init --restore)M3 — Hosted control plane
t-<8-chars>tenant IDs,t-XXXX:slugwsid format with bare-form backwards compat)GEMBA_VAULT_KEY)make vm-imageskipinternal/dispatchRuntime strategy: native (preserved) + firecrackerASDD Phase 1 — Spec Kit interception
/tasks+/implementslash command templates that route throughbd/gemba spec reconciletasks.md/todo.md(Write/Edit/NotebookEdit/Bash); preserves rejected payload viaGEMBA_REJECTED_PAYLOADfor the futuregm-v0sp.21analyzerspec_strict_no_tasks_md(inheritsspec_strict) +gemba constitution lint --strictreports findingsgemba spec preflightrefuses/implementwhentasks.mdexists;--allow-tasks-mdbypass with audit eventgemba init --asdd/--update-claude-mdTest plan
go build ./...cleango test ./internal/...— all subpackages pass on macOS (firecracker via fallback; nftables tests gated on linux+root)GOOS=linux go build -tags linux ./internal/adapter/firecracker/...TestOpenAPISpec_DocsCopyMatches)gemba serve --embedded-doltend-to-end against fresh tempdirgemba login --githubagainst a registered OAuth appNotes for reviewers
gm-v0sp.21analyzer) is filed but not implemented — it consumes the rejected-payload seam this PR ships.gm-o9t8.4.🤖 Generated with Claude Code