Onboarding v2: setup --json surface, grant-aware apply, device-grant pair cleanup - #349
Merged
Merged
Conversation
Completes what #328's harness repoint missed: pairE2E.int.test.ts and fakePairingService.ts still spoke the old anonymous-bootstrap pairing protocol, so both E2E tests failed at head (no /auth/device/authorize on the fake service — no user code ever printed). Masked until now by the CAP-601 exit-code leak that kept CI red-blind. The fake service now serves the RFC 8628 device-grant surface (/auth/device/authorize + /auth/device/token with a per-device queue) alongside the authenticated owned-connection broker surface the CAP-384 grant ceremony drives, and the tests run the real two-phase flow: device approval completes the session, the grant ceremony answers the key material. Both load-bearing properties unchanged: the zero-durable-files walk on the happy path, and coded PAIR_CODE_EXPIRED with exit 3 and nothing written on expiry. Validated: pairE2E 2/0, tsc clean, FULL suite 1854/0 with exit 0.
Onboarding v2 (docs/onboarding-v2.md) replaces the CLI-driven onboard flow entirely: signup/org/custody now happen in Keep, and the hosted MCP onboard tool does install + pair + plan/apply. The flow-driven onboard machinery this CLI carried (driver, sandbox-session ceremony + worker, plan/apply/edits, agentDocs/clis/copy/deployTargets/providers, and the hidden `capy onboard` command with its --broker-ceremony/--ceremony-worker flags) is dead code. Deletes the entire src/flows/onboard/** directory, src/commands/onboardCommand.ts, the `onboard` command registration and CAPY_FLOW_ONBOARD gate in src/index.ts/src/index-dev.ts, and every test that existed solely to cover that code. `codeForSilentAuthFailure` — the one thing `capy flow run` (checkout) genuinely borrowed from the onboard executors — is relocated to a new src/flows/shared.ts so checkout no longer imports the onboard layer at all. src/flows/client.ts's `NextRequest.observations` field loses its dependency on the deleted onboard/observe.ts in favor of a generic `FlowObservations` type; the class itself (and its `FlowHttpError` export, used by the generic error screen) is otherwise untouched.
…capyCommand capyCommand.ts carried a parallel set of fields and branches (assumeEncryptConsent, onProjectResolved, pinnedOrgId, flowProjectName, noWizardStops, refuseWizardStop, CliOptions.brokerCeremony) that only the now-deleted onboard executors ever set — initializeProjectForFlow, bootstrapProjectForFlow, and syncForFlow were public entry points that existed solely for src/flows/onboard/executors to call. All three, their backing private fields, and every `if (this.noWizardStops)` / `if (this.options.brokerCeremony)` branch they gated (the org/project/branch pickers, the encrypt-consent prompt, the sync conflict menu, the silent-auth fallback) are removed. Every removed branch sat ahead of the plain TTY/--web `else` path in an if/else chain, so that path is unchanged — same prompts, same wizard stops, same order. webMode.ts's brokerCeremonyMode and onboardJsonMode were process-globals set exclusively by onboardCommand.ts (now deleted) and read by errorScreen.ts's displayErrorAndExit. Since nothing can ever set them again, the isBrokerCeremonyMode()/isOnboardJsonMode() branches in displayErrorAndExit collapse to their TTY/--web case unconditionally, and the two mode flags plus their setters/getters are deleted. `human()` survives — capyCommand.ts's own runInitialization/reconcileBranchConflict still call it — simplified to a plain console.log now that the stderr-under-json-mode routing it existed for is gone. Test fallout: the describe blocks that existed only to test initializeProjectForFlow/noWizardStops/brokerCeremony are deleted from capyCommand.test.ts (the plain-TTY/--web sibling test in each block already covers the surviving behavior); errorScreenBrokerCeremony.test.ts tested setBrokerCeremonyMode exclusively and is deleted outright. ERROR_CODES loses FLOW_STOP_UNREACHABLE (its only three throw sites are all in this diff) and FLOW_ENVELOPE_INVALID (onboard/sandboxCeremony.ts's own code, deleted in the prior commit).
…omEnvelope pairCeremony.ts and pairingBrokerClient.ts were the CAP-409 pairing ceremony that capy-cli#328/CAP-566 superseded with the RFC 8628 device-grant rail (deviceAuth.ts/pairDeviceGrant.ts). pairCommand.ts already imports only the device-grant modules — verified with grep before deleting — so nothing in this build reaches runPairCeremony anymore. Their dedicated tests (pairCeremony.test.ts, and pairingBrokerClient.test.ts which only that ceremony imported) go with them. pairCommand.test.ts is untouched: it already tests the current device-grant behavior and only mentions runPairCeremony in a historical doc comment. createOrganizationFromEnvelope (orgCreation.ts) was org creation from a broker-ceremony sealed answer — its only caller was the now-deleted sandboxCeremony.ts. Deleted along with its private helpers (installOrglessThenScopedTokenProvider, codeForAuthFailure) and runCannedCaseAEnrollment, which had no caller left once createOrganizationFromEnvelope was gone (sandboxCeremony.ts was its other caller). createNewOrganization — the TTY/--web org-creation path capyCommand.ts actually uses — is untouched, including its own use of keyServiceOpsFromClient. orgCreationFromEnvelope.test.ts tested nothing else and is deleted outright. INVALID_RECOVERY_PHRASE (types/index.ts, previous commit) was this function's own error code and had no other reference.
Implements the docs/cli-setup-json.md contract: setup plan as pure JSON on stdout with a deterministic plan_hash, apply gated on the hash recomputed at confirm time (PLAN_CHANGED on mismatch), root --json sync when keep.lock exists, coded refusals (EXIT_NEEDS_INPUT) for anything that would prompt. New narrow code path beside the untouched TTY/--web wizard; installGitHooks extracted byte-for-byte from CapyCommand for reuse by the apply path. Tests registered at the isolated-files append marker.
A machine paired via the temporary device-key grant holds no local key.enc/K_local — its key material lives behind the grant daemon — so the apply's resolveProjectKey refused PERMISSION_DENIED on the fresh-paired machine's first confirm. Fall back to the same grant rail capy run rides (fetchGrantedKLocal + resolveProjectKeyFromGrant) when the socket is configured; grant failures keep their own coded errors. Found live by journey run 14.
backupPlaintextEnv printed its notice unconditionally, leaking one prose line ahead of the confirm result and tripping the pure-JSON-stdout law (journey run 15 — the apply itself succeeded, secrets written). Additive quiet flag; the TTY init path prints byte-identically.
…d header pairCommand.ts's header described the demolished anonymous-bootstrap ceremony; rewritten to match the live device-grant design. pairContract.ts loses PAIR_PURPOSE/PAIR_FLOW/PAIR_CEREMONY, parsePairPayload and its validators (zero live importers — verified); PairMachineAnswer* types stay (deviceAuth/installPairedSession/pairKeyMaterial still import them). The test file covering only the removed parser goes with it. Scoped cleanup from CAP-608 recon; drafted by the recon agent, applied post-journey-green.
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.
Lands the onboarding-v2 CLI line onto feat/portability. Contents: the docs/cli-setup-json.md contract implementation (capy setup --json plan / --confirm apply, --json sync, coded refusals, EXIT_NEEDS_INPUT), grant-aware project-key resolution on the apply path (a machine paired via the temporary device-key grant rides the same rail as capy run), pure-JSON-stdout fixes, and removal of dead pre-device-grant pairing machinery with a corrected pairCommand header.
Validation: full suite 1804 pass / 0 fail (exit 0); the end-to-end onboarding-v2 journey (Keep signup → WorkOS device approval → grant ceremony → MCP plan/confirm relay → encrypt → decrypt round-trip byte-match) passed against the local rig with this exact line.