fix: default new workspaces to host GitHub access (#2156)
This commit is contained in:
@@ -10,6 +10,12 @@
|
||||
> workspace automation identity and task Git credential routing are separate settings. Background
|
||||
> automation still uses the workspace connection; tasks may explicitly inherit executor
|
||||
> credentials instead of receiving the managed broker contract.
|
||||
>
|
||||
> Amended by
|
||||
> [ADR-2026-08-02-new-workspace-github-access-defaults](2026-08-02-new-workspace-github-access-defaults.md):
|
||||
> operator-authorized workspace creation may snapshot the host's active authenticated `gh`
|
||||
> host/login as that new workspace's explicit named CLI automation connection. Member-created
|
||||
> workspaces never receive the operator identity automatically.
|
||||
|
||||
- Status: accepted
|
||||
- Date: 2026-07-19
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# ADR-2026-07-27-task-git-credential-policy: Separate GitHub Automation From Task Git Credential Policy
|
||||
|
||||
> Amended by
|
||||
> [ADR-2026-08-02-new-workspace-github-access-defaults](2026-08-02-new-workspace-github-access-defaults.md):
|
||||
> newly created workspaces persist `executor` as their initial task Git policy, while existing
|
||||
> persisted and legacy fallback behavior remains unchanged.
|
||||
|
||||
**Status:** accepted
|
||||
**Date:** 2026-07-27
|
||||
**Area:** backend, frontend, security
|
||||
@@ -19,10 +24,12 @@ control process can start successfully while leaving the child Git helper unable
|
||||
Workspace GitHub automation identity and task Git credential policy are separate workspace-owned
|
||||
settings:
|
||||
|
||||
- `managed` is the default. Kandev injects task/repository-bound broker leases for attached GitHub
|
||||
- `managed` is the backward-compatible fallback for existing missing or invalid settings. Kandev
|
||||
injects task/repository-bound broker leases for attached GitHub
|
||||
repositories. PAT, named GitHub CLI, GitHub App, and migration-only legacy connections remain
|
||||
automation methods rather than task-policy values.
|
||||
- `executor` is the explicit inheritance mode. Kandev injects no GitHub broker helper or `gh` shim.
|
||||
- `executor` is the initial mode persisted for newly created workspaces and remains an explicit
|
||||
inheritance mode. Kandev injects no GitHub broker helper or `gh` shim.
|
||||
Local and Worktree tasks use credentials visible on the Kandev host; Docker, SSH, Sprites, and
|
||||
other remote tasks use credentials configured in that executor.
|
||||
- An explicit executor-profile `GITHUB_TOKEN` or `GH_TOKEN` remains an unmanaged override. It takes
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# ADR-2026-08-02-new-workspace-github-access-defaults: Bootstrap New Workspaces From Host GitHub Access
|
||||
|
||||
**Status:** accepted
|
||||
**Date:** 2026-08-02
|
||||
**Area:** backend, frontend, security
|
||||
|
||||
## Context
|
||||
|
||||
New workspaces currently start with managed task Git credentials but no workspace GitHub
|
||||
connection. Launching a task with an attached GitHub repository therefore asks the credential
|
||||
broker for a lease that cannot be issued, even when the Kandev host already has an authenticated
|
||||
`gh` account and usable local Git credentials. The first task on a fresh self-hosted installation
|
||||
can fail before the agent starts.
|
||||
|
||||
The host `gh` account is an operator credential. Kandev already prevents non-admin members from
|
||||
selecting it, so workspace bootstrapping must preserve that boundary instead of granting the host
|
||||
identity to every authenticated creator.
|
||||
|
||||
## Decision
|
||||
|
||||
Every newly created workspace attempts to persist **Inherit executor Git credentials** (`executor`)
|
||||
as its initial task Git access policy. When that persistence succeeds, Local and Worktree tasks use
|
||||
host-visible Git or SSH credentials; remote tasks use credentials configured in their executor. If
|
||||
the settings write fails, workspace creation remains available and the existing `managed`
|
||||
compatibility fallback applies until the workspace is configured or retried.
|
||||
|
||||
When workspace creation is performed by an internal trusted caller, an auth-disabled synthetic
|
||||
administrator, or a real administrator, Kandev also snapshots the host's active authenticated
|
||||
`gh` account as a named workspace automation connection. It stores the exact host/login selection,
|
||||
not a token and not a live reference to whichever account becomes active later. If `gh` is absent,
|
||||
unauthenticated, or cannot validate the selected account, workspace creation still succeeds with
|
||||
executor task access and disconnected GitHub automation.
|
||||
|
||||
A non-admin member-created workspace receives executor task access but never inherits the server
|
||||
operator's `gh` account automatically. The member or an administrator must configure an allowed
|
||||
workspace automation identity explicitly.
|
||||
|
||||
The initial workspace seeded for a brand-new database follows the same rule. Upgrades do not
|
||||
rewrite any existing workspace connection, persisted task policy, or legacy missing/invalid policy
|
||||
fallback. Existing missing or invalid stored values continue to normalize to `managed` for
|
||||
backward compatibility.
|
||||
|
||||
This decision amends
|
||||
[ADR-2026-07-27-task-git-credential-policy](2026-07-27-task-git-credential-policy.md) only for the
|
||||
default assigned to newly created workspaces, and amends
|
||||
[ADR 0047](0047-github-authentication-ownership.md) only to allow an operator-authorized creation
|
||||
flow to make an explicit named CLI selection automatically.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A fresh local installation can launch its first task without configuring Kandev's credential
|
||||
broker first.
|
||||
- Background GitHub automation works immediately when the host `gh` account is authenticated and
|
||||
the creator is allowed to use operator credentials.
|
||||
- Workspace task access remains explicit and persisted; changing the host's active `gh` account
|
||||
later does not silently change an existing workspace.
|
||||
- A settings persistence failure is logged and leaves the workspace on the existing managed
|
||||
fallback, so a caller must configure or retry task access before relying on executor inheritance.
|
||||
- Remote executors do not inherit host credentials merely because the workspace default is
|
||||
`executor`; they still require executor-specific credentials.
|
||||
- Member-created workspaces remain isolated from the server operator's host identity.
|
||||
- Upgrade behavior is stable, at the cost of different defaults for pre-existing missing settings
|
||||
and newly created workspaces.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Keep managed task access as the default
|
||||
|
||||
Rejected. A new disconnected workspace cannot issue the lease managed mode requires, so ordinary
|
||||
task creation fails before the agent starts.
|
||||
|
||||
### Auto-bind the host account to member-created workspaces
|
||||
|
||||
Rejected. It bypasses the existing operator-only CLI binding rule and can expose repositories the
|
||||
member was not intended to access.
|
||||
|
||||
### Resolve the currently active host account on every task launch
|
||||
|
||||
Rejected. A later `gh auth switch` would silently change workspace identity and recreate the
|
||||
cross-workspace ambient-credential coupling that ADR 0047 removed.
|
||||
|
||||
### Migrate existing unconfigured workspaces
|
||||
|
||||
Rejected. Existing installations may rely on managed routing or deliberate disconnection. The new
|
||||
defaults apply prospectively without changing saved or legacy behavior.
|
||||
@@ -98,3 +98,4 @@ Read individual ADRs for full context. Create new ones via `/record decision` or
|
||||
| 2026-08-01-validate-mcp-tool-arguments | [Validate MCP Tool Arguments at the Shared Server Boundary](2026-08-01-validate-mcp-tool-arguments.md) | accepted | backend, protocol | 2026-08-01 |
|
||||
| 2026-08-01-global-run-scheduler-ownership | [Separate Global Run Dispatch from Office Maintenance](2026-08-01-global-run-scheduler-ownership.md) | accepted | backend, workflow | 2026-08-01 |
|
||||
| 2026-08-01-share-artifact-locale | [Shared-Task Artifacts Render in the Creator's Locale](2026-08-01-share-artifact-locale.md) | accepted | backend | 2026-08-01 |
|
||||
| 2026-08-02-new-workspace-github-access-defaults | [Bootstrap New Workspaces From Host GitHub Access](2026-08-02-new-workspace-github-access-defaults.md) | accepted | backend, frontend, security | 2026-08-02 |
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
spec: docs/specs/integrations/github-authentication.md
|
||||
created: 2026-08-02
|
||||
status: completed
|
||||
---
|
||||
|
||||
# Implementation Plan: New Workspace GitHub Access Defaults
|
||||
|
||||
## Overview
|
||||
|
||||
The confirmed root cause is that `github_workspace_settings` resolves a missing task policy to
|
||||
`managed`, while a new workspace has no `github_workspace_connections` row. Task launch therefore
|
||||
requests a broker lease that fails with `ErrGitHubNotConfigured` before the agent starts. The fix
|
||||
will persist executor task access for each new workspace, optionally snapshot an operator-authorized
|
||||
active host `gh` account as a named CLI connection, and leave every existing installation unchanged.
|
||||
|
||||
The implementation begins with failing backend and browser regressions, then adds the smallest
|
||||
workspace-initialization seam and GitHub persistence behavior required to make them pass. Public
|
||||
documentation follows after the behavior is green.
|
||||
|
||||
---
|
||||
|
||||
## Backend
|
||||
|
||||
### GitHub workspace default initializer
|
||||
|
||||
- Add a focused workspace-default initialization path under
|
||||
`apps/backend/internal/github/` (prefer a new `workspace_defaults.go` with sibling tests rather
|
||||
than growing `service_connections.go`).
|
||||
- Persist `TaskGitCredentialsModeExecutor` for a workspace created after this feature is active.
|
||||
Keep `defaultWorkspaceSettings`, `normalizeTaskGitCredentialsMode`, existing schema defaults, and
|
||||
upgrade fallbacks on `managed`; those are the backward-compatibility path for existing missing or
|
||||
invalid state.
|
||||
- Discover the currently active account through the service's injectable CLI account source. Only
|
||||
an internal caller, synthetic administrator, or real administrator may receive the host account;
|
||||
reuse the existing `requireGHCLIOperator` boundary for member denial.
|
||||
- Validate and store the exact `github.com` host/login as a `gh_cli` connection without persisting a
|
||||
bearer token. No active/valid CLI account is a soft condition: executor settings are still saved
|
||||
and workspace creation succeeds disconnected.
|
||||
- Record whether the GitHub schema belongs to a brand-new database before `NewStore` initializes
|
||||
its tables. Expose a one-shot startup method that initializes the already-seeded default
|
||||
workspace only in that fresh-database case. Existing databases, including workspaces with no
|
||||
settings row, are not backfilled or rebound.
|
||||
|
||||
### Workspace creation seam
|
||||
|
||||
- Add a narrow `WorkspaceDefaultsInitializer` interface and setter in
|
||||
`apps/backend/internal/task/service/service.go`.
|
||||
- Call it in `CreateWorkspace` after the workspace transaction succeeds and before
|
||||
`workspace.created` is published, so an immediately created task observes executor mode rather
|
||||
than racing an event subscriber. The initializer owns optional-CLI degradation; a returned
|
||||
persistence error is logged as a workspace-default initialization failure.
|
||||
- Wire the GitHub service into this seam in `apps/backend/internal/backendapp/services.go` after
|
||||
workspace authorization and connection-secret infrastructure are installed. Invoke the
|
||||
fresh-database bootstrap at the same composition point for the repository-seeded initial
|
||||
workspace.
|
||||
- Do not use a `workspace.created` subscription as the correctness boundary: the NATS-backed bus
|
||||
need not finish a subscriber before the create request can be followed by task launch.
|
||||
|
||||
### Upgrade and security boundaries
|
||||
|
||||
- Do not modify existing `github_workspace_connections` or
|
||||
`github_workspace_settings.task_git_credentials_mode` rows.
|
||||
- Do not change the current `managed` normalization for legacy missing/invalid values.
|
||||
- Do not auto-bind host `gh` for a non-admin member context.
|
||||
- Do not copy or persist the resolved CLI token, and do not follow later host active-account
|
||||
changes for an already-created workspace.
|
||||
|
||||
---
|
||||
|
||||
## Frontend
|
||||
|
||||
No React component, route, API client, state slice, copy, or responsive composition changes are
|
||||
required. The existing Workspace GitHub access summary and connection dialog already render the
|
||||
backend's saved `executor` and `gh_cli` values.
|
||||
|
||||
### Mobile parity contract
|
||||
|
||||
The nearest shipped mobile exemplar remains
|
||||
`apps/web/e2e/tests/integrations/mobile-github-workspace-settings.spec.ts`: the existing full-height,
|
||||
single-scroll-owner connection drawer and 44px task-access controls are unchanged. Update that
|
||||
scenario to prove the initial summary reads **Inherit executor Git credentials**. Because this is a
|
||||
persisted backend-default change with no layout, navigation, scroll, touch, or composition change,
|
||||
the focused existing mobile scenario satisfies mobile parity without a new mobile surface.
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
- **What:** a new workspace always receives persisted executor task access; an internal/admin
|
||||
creator with a valid active CLI account also receives an exact named CLI connection.
|
||||
**File:** `apps/backend/internal/github/workspace_defaults_test.go`.
|
||||
**How:** SQLite-backed service tests with injectable CLI account/token validation boundaries.
|
||||
- **What:** member creation never receives the server operator's CLI identity; unavailable,
|
||||
unauthenticated, or invalid CLI discovery degrades to disconnected automation without losing
|
||||
executor task access.
|
||||
**File:** `apps/backend/internal/github/workspace_defaults_test.go`.
|
||||
**How:** table-driven service tests using real store reads and synthetic/admin/member contexts.
|
||||
- **What:** a database with pre-existing GitHub schema/settings is not treated as fresh and its
|
||||
missing or managed behavior is not rewritten, while a genuinely fresh schema initializes the
|
||||
seeded workspace once.
|
||||
**Files:** `apps/backend/internal/github/workspace_defaults_test.go`,
|
||||
`apps/backend/internal/github/workspace_settings_test.go`.
|
||||
**How:** fresh and replayed SQLite store setup with row-level assertions.
|
||||
- **What:** task-service workspace creation runs default initialization before publishing the
|
||||
created event.
|
||||
**File:** `apps/backend/internal/task/service/service_resources_test.go`.
|
||||
**How:** a recording initializer and event bus assert call order and error handling.
|
||||
- **What:** backend composition wires runtime workspace creation and fresh-install startup to the
|
||||
GitHub initializer.
|
||||
**File:** `apps/backend/internal/backendapp/services_github_defaults_test.go` (or the nearest
|
||||
existing service-composition test if a smaller harness is available during RED).
|
||||
**How:** real SQLite repositories with injected GitHub account resolution and stored-state
|
||||
assertions.
|
||||
- **What:** executor policy skips broker lease injection.
|
||||
**File:** `apps/backend/internal/orchestrator/executor/executor_credentials_test.go`.
|
||||
**How:** retain the existing `executor inheritance` regression as downstream contract evidence;
|
||||
add no duplicate test unless the new integration test cannot prove the no-lease outcome.
|
||||
|
||||
Targeted backend verification:
|
||||
|
||||
```bash
|
||||
cd apps/backend && go test -tags fts5 ./internal/github ./internal/task/service ./internal/backendapp ./internal/orchestrator/executor -run 'Test.*Workspace.*Defaults|Test.*Workspace.*Initializ|TestTaskGitCredentialModeDefaultsManaged|TestApplyGitCredentialSnapshotUsesExecutorSources' -count=1
|
||||
```
|
||||
|
||||
## E2E Tests
|
||||
|
||||
- **Scenario:** GIVEN a new workspace and an active mock CLI account, WHEN an administrator creates
|
||||
the workspace and opens Workspace GitHub access, THEN the UI shows that exact CLI automation
|
||||
identity and **Inherit executor Git credentials**.
|
||||
**File:** `apps/web/e2e/tests/integrations/github-authentication.spec.ts`.
|
||||
**What to verify:** automation source/login and task-access summary through the rendered settings
|
||||
page, with backend seeding only for the CLI-account precondition.
|
||||
- **Scenario:** GIVEN the initial fresh E2E workspace, WHEN desktop and mobile users open Workspace
|
||||
GitHub access, THEN the existing summary and dialog/drawer show executor inheritance as the
|
||||
initial task policy.
|
||||
**Files:** `apps/web/e2e/tests/integrations/github-workspace-settings.spec.ts`,
|
||||
`apps/web/e2e/tests/integrations/mobile-github-workspace-settings.spec.ts`.
|
||||
**What to verify:** desktop and mobile report the same saved policy; existing mobile geometry,
|
||||
scroll ownership, safe-area containment, and touch targets remain green.
|
||||
|
||||
Targeted browser verification after the fresh-worktree install:
|
||||
|
||||
```bash
|
||||
cd apps && pnpm install --frozen-lockfile
|
||||
cd apps && pnpm --filter @kandev/web run e2e:run -- tests/integrations/github-authentication.spec.ts tests/integrations/github-workspace-settings.spec.ts
|
||||
cd apps && pnpm --filter @kandev/web run e2e:run -- --project mobile-chrome tests/integrations/mobile-github-workspace-settings.spec.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Public Documentation
|
||||
|
||||
Update `docs/public/integrations.md` so **Inherit executor Git credentials** is documented as the
|
||||
new-workspace default, managed credentials are described as an opt-in workspace policy, active host
|
||||
`gh` auto-binding is limited to operator-authorized creation, remote executors remain responsible
|
||||
for their own credentials, and upgrades are unchanged.
|
||||
|
||||
Validate with:
|
||||
|
||||
```bash
|
||||
node --test scripts/validate-public-docs.test.mjs
|
||||
node scripts/validate-public-docs.mjs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification Results
|
||||
|
||||
- `cd apps/backend && go test -tags fts5 ./internal/github ./internal/task/service ./internal/backendapp ./internal/orchestrator/executor -run 'Test.*Workspace.*Defaults|Test.*Workspace.*Initializ|TestTaskGitCredentialModeDefaultsManaged|TestApplyGitCredentialSnapshotUsesExecutorSources' -count=1` — 12 passed in 4 packages.
|
||||
- `cd apps/backend && go test -tags fts5 ./internal/github ./internal/task/service ./internal/backendapp ./internal/orchestrator/executor -count=1` — 2402 passed in 4 packages.
|
||||
- `cd apps && pnpm --filter @kandev/web run e2e:run -- tests/integrations/github-authentication.spec.ts tests/integrations/github-workspace-settings.spec.ts` — 9 passed.
|
||||
- `cd apps && pnpm --filter @kandev/web run e2e:run -- --project mobile-chrome tests/integrations/mobile-github-workspace-settings.spec.ts` — 2 passed.
|
||||
- `cd apps/backend && make lint` — 0 issues.
|
||||
- `cd apps && pnpm --filter @kandev/web run typecheck` — passed.
|
||||
- `cd apps && pnpm --filter @kandev/web run lint` — passed.
|
||||
- `cd apps && pnpm --filter @kandev/web run i18n:check && pnpm --filter @kandev/web run i18n:ratchet` — passed.
|
||||
- `node --test scripts/validate-public-docs.test.mjs` — 58 passed.
|
||||
- `node scripts/validate-public-docs.mjs` — 41 published docs pages validated.
|
||||
- `cd apps && pnpm run format:check` and `git diff --check` — passed.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Waves And Parallel Candidates
|
||||
|
||||
Execution is sequential in the primary conversation; no task is marked parallel-safe because the
|
||||
RED tests define the contracts consumed by the backend implementation and documentation must match
|
||||
the final green behavior.
|
||||
|
||||
- [x] [Task 01: Add failing default-behavior regressions](task-01-default-behavior-regressions.md)
|
||||
- [x] [Task 02: Implement workspace GitHub defaults](task-02-workspace-github-defaults.md)
|
||||
- [x] [Task 03: Update public integration documentation](task-03-public-integration-documentation.md)
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: "01-default-behavior-regressions"
|
||||
title: "Default behavior regressions"
|
||||
status: completed
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/integrations/github-authentication.md"
|
||||
---
|
||||
|
||||
# Task 01: Default behavior regressions
|
||||
|
||||
- **Acceptance:** Backend tests reproduce that new workspaces must persist executor task access,
|
||||
bind a valid active CLI account only for internal/admin creators, and preserve existing-install
|
||||
state.
|
||||
- **Acceptance:** Task-service/composition tests prove initialization occurs before workspace-created
|
||||
publication and covers the repository-seeded initial workspace on a fresh database.
|
||||
- **Acceptance:** Desktop and existing mobile Playwright scenarios assert the rendered executor
|
||||
default and exact auto-bound CLI identity; each new expectation fails for the intended current
|
||||
behavior before production code changes.
|
||||
- **Verification:** Run the focused Go command below and record the expected assertion failures (not
|
||||
compile failures). After adding the Playwright expectations and installing dependencies, run the
|
||||
focused desktop/mobile commands and record their expected failures.
|
||||
|
||||
```bash
|
||||
cd apps/backend && go test -tags fts5 ./internal/github ./internal/task/service ./internal/backendapp -run 'Test.*Workspace.*Defaults|Test.*Workspace.*Initializ' -count=1
|
||||
cd apps && pnpm install --frozen-lockfile
|
||||
cd apps && pnpm --filter @kandev/web run e2e:run -- tests/integrations/github-authentication.spec.ts tests/integrations/github-workspace-settings.spec.ts
|
||||
cd apps && pnpm --filter @kandev/web run e2e:run -- --project mobile-chrome tests/integrations/mobile-github-workspace-settings.spec.ts
|
||||
```
|
||||
|
||||
- **Files likely touched:**
|
||||
- `apps/backend/internal/github/workspace_defaults_test.go`
|
||||
- `apps/backend/internal/github/workspace_settings_test.go`
|
||||
- `apps/backend/internal/task/service/service_resources_test.go`
|
||||
- `apps/backend/internal/backendapp/services_github_defaults_test.go`
|
||||
- `apps/web/e2e/tests/integrations/github-authentication.spec.ts`
|
||||
- `apps/web/e2e/tests/integrations/github-workspace-settings.spec.ts`
|
||||
- `apps/web/e2e/tests/integrations/mobile-github-workspace-settings.spec.ts`
|
||||
- **Dependencies:** None.
|
||||
- **Parallelism:** sequential; these RED contracts must exist before Task 02 changes production.
|
||||
- **Inputs:** spec `What`, `Task Git credential resolution`, `Failure Modes`, `Scenarios`, and plan
|
||||
`Tests`/`E2E Tests`; existing `requireGHCLIOperator`, mock GitHub account APIs, task-service
|
||||
workspace event ordering, and executor inheritance tests.
|
||||
- **Risks:** The E2E mock must exercise injected CLI discovery rather than the developer host's real
|
||||
`gh` configuration. A test that passes without the production default initializer must be revised
|
||||
before leaving RED.
|
||||
- **Output contract:** Summarize files changed, every RED command and expected failure, unexpected
|
||||
blockers, and risks; mark this task and its plan checkbox complete while leaving production code
|
||||
untouched.
|
||||
|
||||
## Results
|
||||
|
||||
- Added backend regressions for executor persistence, exact active CLI binding without token
|
||||
storage, member isolation, soft unavailable/invalid CLI behavior, existing-state preservation,
|
||||
fresh-install bootstrap, and task-service initialization-before-publication ordering.
|
||||
- Added desktop and mobile expectations that create a new workspace and verify executor inheritance;
|
||||
the GitHub authentication scenario now proves an active mock CLI account is auto-bound.
|
||||
- The initial RED backend command failed at the expected undefined initializer seam before
|
||||
production changes; its first failure was the missing `WorkspaceDefaultsInitializer` seam, and
|
||||
the final focused suite is green.
|
||||
- The first desktop command was run after the production edit and exposed that the E2E reset fixture
|
||||
intentionally deletes workspace settings, restoring the legacy managed fallback: five GitHub
|
||||
authentication tests and three workspace-settings tests passed, while the executor-summary test
|
||||
received `Managed workspace credentials`. The scenarios were corrected to create a genuinely new
|
||||
workspace for default assertions, and the final desktop command passed nine tests.
|
||||
- The first valid mobile command was run after the fixture correction and passed two tests; no
|
||||
separate pre-production mobile RED assertion was captured because the shared reset behavior made
|
||||
the original expectation invalid before the implementation could be evaluated.
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: "02-workspace-github-defaults"
|
||||
title: "Workspace GitHub defaults"
|
||||
status: completed
|
||||
wave: 2
|
||||
depends_on: ["01-default-behavior-regressions"]
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/integrations/github-authentication.md"
|
||||
---
|
||||
|
||||
# Task 02: Workspace GitHub defaults
|
||||
|
||||
- **Acceptance:** Every newly created workspace and the initial workspace on a genuinely fresh
|
||||
database persist executor task access before they can launch a task; existing installations are
|
||||
not backfilled or rewritten.
|
||||
- **Acceptance:** Internal/synthetic-admin/real-admin creation stores a validated active CLI
|
||||
host/login without a token, while member creation and absent/invalid CLI state remain disconnected
|
||||
and still succeed.
|
||||
- **Acceptance:** All Task 01 backend, desktop, and mobile regressions pass, and executor mode issues
|
||||
no managed credential lease.
|
||||
- **Verification:** Run the targeted backend and managed desktop/mobile E2E commands exactly as
|
||||
listed below after the final production edit.
|
||||
|
||||
```bash
|
||||
cd apps/backend && go test -tags fts5 ./internal/github ./internal/task/service ./internal/backendapp ./internal/orchestrator/executor -run 'Test.*Workspace.*Defaults|Test.*Workspace.*Initializ|TestTaskGitCredentialModeDefaultsManaged|TestApplyGitCredentialSnapshotUsesExecutorSources' -count=1
|
||||
cd apps && pnpm --filter @kandev/web run e2e:run -- tests/integrations/github-authentication.spec.ts tests/integrations/github-workspace-settings.spec.ts
|
||||
cd apps && pnpm --filter @kandev/web run e2e:run -- --project mobile-chrome tests/integrations/mobile-github-workspace-settings.spec.ts
|
||||
```
|
||||
|
||||
- **Files likely touched:**
|
||||
- `apps/backend/internal/github/store.go`
|
||||
- `apps/backend/internal/github/workspace_defaults.go`
|
||||
- `apps/backend/internal/github/workspace_defaults_test.go`
|
||||
- `apps/backend/internal/github/workspace_settings_test.go`
|
||||
- `apps/backend/internal/task/service/service.go`
|
||||
- `apps/backend/internal/task/service/service_resources.go`
|
||||
- `apps/backend/internal/task/service/service_resources_test.go`
|
||||
- `apps/backend/internal/backendapp/services.go`
|
||||
- `apps/backend/internal/backendapp/services_github_defaults_test.go`
|
||||
- `apps/web/e2e/tests/integrations/github-authentication.spec.ts`
|
||||
- `apps/web/e2e/tests/integrations/github-workspace-settings.spec.ts`
|
||||
- `apps/web/e2e/tests/integrations/mobile-github-workspace-settings.spec.ts`
|
||||
- **Dependencies:** Task 01.
|
||||
- **Parallelism:** sequential; it changes the shared service-composition and GitHub workspace
|
||||
persistence path.
|
||||
- **Inputs:** Task 01 RED evidence; ADR-2026-08-02-new-workspace-github-access-defaults; existing
|
||||
GitHub connection validation/commit functions, mock account injection, workspace authorization,
|
||||
and task-service workspace creation ordering.
|
||||
- **Risks:** Do not use ambient host `gh` in E2E, do not auto-bind members, do not reinterpret legacy
|
||||
missing settings as executor, and do not make CLI unavailability fail workspace creation.
|
||||
- **Output contract:** Summarize implementation, actual files changed, exact GREEN command results
|
||||
and Playwright artifact paths, security/external-side-effect boundaries, and remaining risks;
|
||||
update this task and `plan.md` statuses/results.
|
||||
|
||||
## Results
|
||||
|
||||
- Added `Store.EnsureWorkspaceExecutorDefaults` and fresh-schema detection; existing missing or
|
||||
invalid rows still resolve through the managed compatibility fallback.
|
||||
- Added the GitHub workspace-default initializer, operator/admin boundary, injectable CLI account
|
||||
discovery, token-free named connection commit, soft CLI degradation, and one-shot seeded-workspace
|
||||
bootstrap.
|
||||
- Added the task-service initializer seam and wired it before `workspace.created`; backend composition
|
||||
wires the GitHub service and invokes fresh-install bootstrap after authorization/secret setup.
|
||||
- Focused Go suite: `12 passed in 4 packages`.
|
||||
- Scoped Go suite: `2402 passed in 4 packages`.
|
||||
- Desktop GitHub authentication/settings E2E: `9 passed` in the combined run after the fixture
|
||||
correction; focused settings rerun `4 passed`.
|
||||
- Mobile GitHub settings E2E: `2 passed`.
|
||||
- No CLI bearer token is persisted; member-created workspaces never list or bind operator accounts.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
---
|
||||
id: "03-public-integration-documentation"
|
||||
title: "Public integration documentation"
|
||||
status: completed
|
||||
wave: 3
|
||||
depends_on: ["02-workspace-github-defaults"]
|
||||
plan: "plan.md"
|
||||
spec: "../../specs/integrations/github-authentication.md"
|
||||
---
|
||||
|
||||
# Task 03: Public integration documentation
|
||||
|
||||
- **Acceptance:** Public docs identify executor credentials as the new-workspace default and
|
||||
managed workspace credentials as an explicit later choice.
|
||||
- **Acceptance:** The docs explain operator-only active host `gh` auto-binding, disconnected fallback,
|
||||
remote-executor responsibility, and unchanged upgrade behavior without implying that a token is
|
||||
stored.
|
||||
- **Verification:** Run both public-doc validators.
|
||||
|
||||
```bash
|
||||
node --test scripts/validate-public-docs.test.mjs
|
||||
node scripts/validate-public-docs.mjs
|
||||
```
|
||||
|
||||
- **Files likely touched:** `docs/public/integrations.md`.
|
||||
- **Dependencies:** Task 02.
|
||||
- **Parallelism:** sequential; documentation must reflect the final green behavior.
|
||||
- **Inputs:** amended GitHub-authentication spec, ADR-2026-08-02-new-workspace-github-access-defaults,
|
||||
and Task 02 results.
|
||||
- **Risks:** Avoid saying Local/Worktree behavior also applies to Docker/SSH/cloud executors, and do
|
||||
not describe existing workspaces as migrated.
|
||||
- **Output contract:** Summarize the doc changes and exact validator results; mark this task and the
|
||||
plan complete and synchronize `## Verification Results`.
|
||||
|
||||
## Results
|
||||
|
||||
- Updated `docs/public/integrations.md` to document executor inheritance as the new-workspace
|
||||
default, managed credentials as opt-in, operator/admin-only active `gh` auto-binding, soft
|
||||
disconnected fallback, remote-executor credential responsibility, and unchanged upgrades.
|
||||
- Public-doc test suite: `58 passed`.
|
||||
- Public-doc validator: `Validated 41 published docs pages.`
|
||||
@@ -70,6 +70,8 @@ Open the workspace GitHub settings. **Workspace automation** offers three connec
|
||||
|
||||
A workspace has one active automation connection at a time. Replacing it changes the identity used by repository discovery, watches, background work, and task GitHub access in that workspace only. Disconnecting a CLI connection does not sign the host out of `gh`; disconnecting an App connection removes only the workspace binding and does not uninstall the App from GitHub.
|
||||
|
||||
New workspaces created by an operator or an internal trusted flow start with **Inherit executor Git credentials** for task Git access when the settings write succeeds. When the backend host has an authenticated active `gh` account, the new workspace also snapshots that exact host/login as its named GitHub CLI automation connection; Kandev stores the selection, never the CLI token. If `gh` is unavailable or unauthenticated, creation still succeeds with executor inheritance and no automation connection. If the settings write itself fails, workspace creation remains available but the existing managed compatibility fallback applies until the workspace is configured or retried. Authenticated non-admin members receive the executor default but never the operator's host identity.
|
||||
|
||||
Workspaces migrated from an older Kandev release may temporarily use a compatibility connection
|
||||
named `legacy_shared`. It continues using the deployment's existing authenticated `gh` account,
|
||||
`GITHUB_TOKEN`/`GH_TOKEN`, or legacy stored PAT for both GitHub API calls and workspace-isolated
|
||||
@@ -114,14 +116,15 @@ select **Save changes** once. GitHub App creation, import, and installation rema
|
||||
GitHub workflows. The help control beside **Task Git access** explains the effective credential
|
||||
path on desktop hover or focus and in a touch-accessible drawer on mobile.
|
||||
|
||||
- **Managed workspace credentials** (the default) uses the selected workspace PAT, named GitHub
|
||||
- **Managed workspace credentials** (an opt-in policy) uses the selected workspace PAT, named GitHub
|
||||
CLI account, or GitHub App through Kandev's short-lived, task/repository-scoped broker. Kandev
|
||||
configures `agentctl` as Git's credential helper so an attached repository can redeem its
|
||||
matching lease on demand; the returned credential is not written to the repository or Git
|
||||
configuration. A separate broker-aware shim handles `gh`. The task receives neither the stored
|
||||
PAT nor an App private key. An executor-profile `GH_TOKEN` or `GITHUB_TOKEN` deliberately takes
|
||||
precedence for that task.
|
||||
- **Inherit executor Git credentials** does not install Kandev's broker helper or `gh` shim. Local
|
||||
- **Inherit executor Git credentials** is the default for newly created workspaces and does not
|
||||
install Kandev's broker helper or `gh` shim. Local
|
||||
and Worktree tasks use credentials already visible to the host Git process (including SSH).
|
||||
Docker, SSH, and cloud tasks use only credentials intentionally configured in that executor.
|
||||
For Kandev-managed GitHub checkouts, Local and Worktree preparation also updates `origin` to the
|
||||
@@ -252,7 +255,7 @@ it.
|
||||
|
||||
### Upgrade and recovery
|
||||
|
||||
Workspaces that existed when workspace authentication was introduced receive a **Legacy shared** connection so upgrades do not immediately lose GitHub access. It preserves the previous installation-wide resolution behavior while the workspace is migrated. New workspaces start disconnected. After a legacy workspace selects a PAT, named CLI account, or App installation, it cannot return to legacy mode. Copying a workspace never copies authentication or App installation bindings.
|
||||
Workspaces that existed when workspace authentication was introduced receive a **Legacy shared** connection so upgrades do not immediately lose GitHub access. It preserves the previous installation-wide resolution behavior while the workspace is migrated. Existing workspaces and their saved task-access policies are not rewritten by the new-workspace defaults. After a legacy workspace selects a PAT, named CLI account, or App installation, it cannot return to legacy mode. Copying a workspace never copies authentication or App installation bindings.
|
||||
|
||||
Legacy shared resolution checks an authenticated host `gh` CLI first, then backend `GITHUB_TOKEN`, backend `GH_TOKEN`, and finally the old stored `GITHUB_TOKEN`/`github_token` secret. Those ambient sources are migration compatibility only; configure an explicit workspace connection to make identity and access deterministic.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
status: building
|
||||
created: 2026-07-19
|
||||
amended: 2026-08-01
|
||||
amended: 2026-08-02
|
||||
owner: Kandev
|
||||
---
|
||||
|
||||
@@ -50,6 +50,18 @@ automation under different GitHub Apps without operating separate Kandev deploym
|
||||
workspace automation connection. **Inherit executor Git credentials** injects no GitHub broker
|
||||
helper or `gh` shim: Local and Worktree tasks use host-visible Git/SSH credentials, while remote
|
||||
tasks use credentials configured in that executor.
|
||||
- Every newly created workspace attempts to persist **Inherit executor Git credentials** as its
|
||||
initial task policy. After a successful settings write, if creation is performed by an internal
|
||||
trusted caller, an auth-disabled synthetic administrator, or a real administrator while host
|
||||
`gh` has an authenticated active account, Kandev also stores that exact host/login as the new
|
||||
workspace's named CLI automation connection. Non-admin member-created workspaces never receive
|
||||
the server operator's CLI identity automatically.
|
||||
- If host `gh` is absent, unauthenticated, or cannot validate its active account, workspace creation
|
||||
still succeeds with executor task access and disconnected GitHub automation. If the executor
|
||||
settings write fails, creation still succeeds but the existing managed compatibility fallback
|
||||
remains until the workspace is configured or retried. Existing workspaces are never migrated to
|
||||
these defaults; their connection, persisted policy, and legacy missing/invalid policy fallback
|
||||
remain unchanged.
|
||||
- Workspace settings present the automation identity and task Git credential routing as one
|
||||
**Workspace GitHub access** group. The page shows a compact read-only summary of both effective
|
||||
choices; the existing **Change GitHub connection** dialog contains the full controls for the
|
||||
@@ -106,8 +118,11 @@ automation under different GitHub Apps without operating separate Kandev deploym
|
||||
- The unpublished `KANDEV_GITHUB_APP_*` configuration introduced on this branch is removed. Setting
|
||||
those variables does not create or configure a registration; operators use the guided import
|
||||
flow for an App they already own.
|
||||
- Existing released workspaces migrate to `legacy_shared`; new workspaces start disconnected.
|
||||
Once a workspace leaves legacy mode it cannot return. The unpublished singleton registration
|
||||
- Existing released workspaces migrate to `legacy_shared`; upgrades do not rewrite their
|
||||
connection or task policy. New operator-authorized workspaces use the active authenticated host
|
||||
`gh` account when one can be validated, while member-created workspaces and workspaces created
|
||||
without usable host `gh` remain disconnected. Once a workspace leaves legacy mode it cannot
|
||||
return. The unpublished singleton registration
|
||||
schema on this branch is rewritten directly and receives no compatibility migration. A valid
|
||||
legacy connection supplies both the existing API client abstraction and an in-memory Git
|
||||
transport credential so provider-backed repositories can be rematerialized from legacy shared
|
||||
@@ -218,8 +233,8 @@ The non-secret operational settings row adds `task_git_credentials_mode`, with a
|
||||
|
||||
| Value | Behavior |
|
||||
| --- | --- |
|
||||
| `managed` | Default. Inject the workspace broker contract for attached GitHub repositories unless an explicit executor-profile token overrides it. |
|
||||
| `executor` | Inject no Kandev GitHub helper or `gh` shim; use credentials available where the selected executor runs. |
|
||||
| `managed` | Inject the workspace broker contract for attached GitHub repositories unless an explicit executor-profile token overrides it. Existing missing/invalid values continue to normalize here for upgrade compatibility. |
|
||||
| `executor` | Default persisted for newly created workspaces. Inject no Kandev GitHub helper or `gh` shim; use credentials available where the selected executor runs. |
|
||||
|
||||
Missing or invalid persisted values normalize to `managed`. Workspace-settings copy includes this
|
||||
policy because it is operational configuration, not authentication material.
|
||||
@@ -359,6 +374,11 @@ post-signature processing failures produce `failing`; a later valid successful d
|
||||
### Task Git credential resolution
|
||||
|
||||
- Missing settings start at `managed`.
|
||||
- New workspace creation attempts to persist `executor` before the workspace is exposed for task
|
||||
launch; a persistence failure leaves the existing managed compatibility fallback in place.
|
||||
- Operator-authorized creation snapshots a validated active host `gh` host/login as a named CLI
|
||||
connection when available; member creation and unavailable/invalid host CLI leave automation
|
||||
disconnected.
|
||||
- `managed + explicit profile GITHUB_TOKEN/GH_TOKEN -> executor_profile`.
|
||||
- `managed + attached GitHub repository + active workspace connection -> workspace broker`.
|
||||
- `executor -> executor-visible credentials`, regardless of PAT/CLI/App automation method.
|
||||
@@ -432,6 +452,12 @@ post-signature processing failures produce `failing`; a later valid successful d
|
||||
truncating, partially merging, or executing a different block.
|
||||
- If executor inheritance is selected but no usable credential exists in that executor, Git/SSH
|
||||
reports its normal authentication failure. Kandev does not probe or guess the actor.
|
||||
- If host `gh` is absent, unauthenticated, or fails active-account validation while a workspace is
|
||||
created, the workspace remains disconnected for automation, retains executor task access, and
|
||||
creation succeeds.
|
||||
- If executor-default persistence fails while a workspace is created, the workspace remains
|
||||
available with the existing managed task-access fallback and a warning for retry/configuration;
|
||||
Kandev does not claim executor inheritance was applied.
|
||||
- If Kandev cannot reconcile a managed checkout's `origin` with the selected task policy, Local and
|
||||
Worktree preparation fails before the agent starts instead of silently using the other policy's
|
||||
transport.
|
||||
@@ -519,6 +545,23 @@ registration and never creates a global default.
|
||||
|
||||
## Scenarios
|
||||
|
||||
- **GIVEN** a brand-new Kandev database and an authenticated active host `gh` account, **WHEN** the
|
||||
initial workspace is seeded, **THEN** its automation connection records that exact CLI
|
||||
host/login, its task access is executor inheritance, and its first task does not request a
|
||||
managed credential lease.
|
||||
- **GIVEN** an administrator or internal trusted caller and an authenticated active host `gh`
|
||||
account, **WHEN** a workspace is created, **THEN** the workspace records that exact named CLI
|
||||
automation connection and executor task access.
|
||||
- **GIVEN** a non-admin member and an authenticated server-operator `gh` account, **WHEN** the
|
||||
member creates a workspace, **THEN** the workspace has executor task access but no automatic
|
||||
GitHub automation connection.
|
||||
- **GIVEN** no usable authenticated host `gh` account, **WHEN** an authorized caller creates a
|
||||
workspace, **THEN** workspace creation succeeds, task access is executor inheritance, and
|
||||
GitHub automation remains disconnected.
|
||||
- **GIVEN** an existing installation with managed, disconnected, or legacy missing workspace
|
||||
settings, **WHEN** Kandev upgrades, **THEN** those connections and task policies remain
|
||||
unchanged.
|
||||
|
||||
- **GIVEN** a workspace GitHub status is visible, **WHEN** the user refreshes it and the status
|
||||
request is still pending, **THEN** the existing workspace content remains visible and usable
|
||||
while the refresh control is busy, on both desktop and mobile.
|
||||
@@ -697,11 +740,15 @@ and the
|
||||
[managed GitHub login-shell repair plan](../../plans/github-managed-tools-login-shell/plan.md),
|
||||
and the
|
||||
[system-service identity guardrails repair plan](../../plans/system-service-identity-guardrails/plan.md).
|
||||
The new-workspace default repair is tracked in
|
||||
[the new workspace GitHub access defaults plan](../../plans/new-workspace-github-access-defaults/plan.md).
|
||||
|
||||
## Decision
|
||||
|
||||
See [ADR-2026-07-21-workspace-selectable-github-app-registrations](../../decisions/2026-07-21-workspace-selectable-github-app-registrations.md)
|
||||
and
|
||||
[ADR-2026-07-27-task-git-credential-policy](../../decisions/2026-07-27-task-git-credential-policy.md).
|
||||
New-workspace defaults are defined by
|
||||
[ADR-2026-08-02-new-workspace-github-access-defaults](../../decisions/2026-08-02-new-workspace-github-access-defaults.md).
|
||||
The system-service ownership boundary is defined by
|
||||
[ADR-2026-07-31-system-service-user-continuity](../../decisions/2026-07-31-system-service-user-continuity.md).
|
||||
|
||||
Reference in New Issue
Block a user