Files
kandev/docs/plans/task-git-credential-policy/plan.md
T
2026-07-27 18:10:12 +01:00

15 KiB

spec, created, status
spec created status
docs/specs/integrations/github-authentication.md 2026-07-27 complete

Implementation Plan: Task Git Credential Policy And Identity

Overview

Fix the confirmed standalone managed-helper wiring failure while separating workspace GitHub automation from task Git credential routing. Persist the policy first, then make initial launch and resume resolve the same effective contract and record a truthful session snapshot. Finish with self-documenting settings, responsive Changes-panel identity disclosure, focused E2E coverage, and public documentation.

Confirmed root causes:

  1. The standalone launcher resolves and starts the agentctl control binary by absolute path, but the per-task Git config stores !agentctl git-credential. The control process only creates/prepends its gh shim directory when broker environment exists at control-process startup; standalone broker data arrives later in the per-instance environment. Therefore child Git cannot find agentctl.
  2. Task environments currently merge GIT_CONFIG_COUNT and indexed keys as ordinary map entries. A Kandev block with count 2 overwrites a host/executor block's count and first two entries, so unrelated tools such as hooks and Git notes disappear. The same defect can hide Docker's safe.directory and URL rewrites. A focused reproduction against CollectAgentEnv produced count 2 where the correct composed result is count 4.
  3. Resume currently does not rebuild the broker contract at all.

Backend

Ordered indexed Git configuration

  • Add a shared backend utility for parsing, validating, composing, and contiguously serializing the GIT_CONFIG_COUNT / GIT_CONFIG_KEY_<n> / GIT_CONFIG_VALUE_<n> protocol.
  • Composition preserves source order, appends higher-precedence entries, and removes only the longest exact suffix/prefix overlap when the same block has already passed through a control process. It never deduplicates arbitrary repeated Git keys because repeated keys can be meaningful.
  • Use structured composition in agent-profile/request merges, route overrides, container environment construction, remote forwarding, and agentctl's parent-plus-instance collection. Ordinary scalar environment variables retain their documented precedence.
  • Keep internal/repoclone isolated: managed clone subprocesses intentionally strip ambient Git configuration and build a closed credential environment.
  • Add a real Git subprocess regression that starts with locstat-shaped core.hooksPath and notes.augment.mergeStrategy entries, adds Kandev's helper entries, makes a commit, and verifies the hook ran. Add focused standalone, Docker, SSH/remote, overlap, precedence, malformed-block, and bounded-count tests.

Workspace policy persistence and description

  • In apps/backend/internal/github/models.go, add TaskGitCredentialsModeManaged, TaskGitCredentialsModeExecutor, and WorkspaceSettings.TaskGitCredentialsMode / UpdateWorkspaceSettingsRequest.TaskGitCredentialsMode.
  • In apps/backend/internal/github/store.go, add github_workspace_settings.task_git_credentials_mode TEXT NOT NULL DEFAULT 'managed' to fresh schema and a fail-loud, idempotent startup migration. Include it in defaults, normalization, reads, upserts, partial patches, and workspace-settings copy.
  • In apps/backend/internal/github/workspace_settings_service.go, reject unknown policy values without mutating the row.
  • Add a non-secret service description that combines the policy with github_workspace_connections and, for Apps, registration display identity. It returns the selected mode, method, and known actor context without resolving or exposing a credential.
  • Extend the existing /api/v1/github/workspace-settings response/update contract. Authorization remains the existing workspace-settings authorization path.

Launch/resume resolution and session snapshot

  • In apps/backend/internal/task/models/models.go, add SessionMetaKeyGitCredentialSnapshot, a versioned GitCredentialSnapshot type, and a JSON-rehydration-safe loader.
  • In apps/backend/internal/orchestrator/executor/executor.go and executor_credentials.go, add a task Git credential policy resolver wired beside GitHubCredentialLeaseIssuer. Resolve one of: workspace, executor_profile, or executor.
  • Move effective GitHub credential configuration out of the middle of buildLaunchAgentRequest so it runs after profile/request environment is final. Reuse the same function from buildResumeRequest after resolveAllRepoInfo.
  • For managed workspace credentials, clear stale broker keys, reset the GitHub HTTPS helper chain with an empty helper before Kandev's helper, set credential.useHttpPath=true, and disable terminal prompts. Executor inheritance removes/omits Kandev broker/helper/shim configuration.
  • Put the candidate snapshot on the in-memory session before lifecycle launch, but persist it only through the existing successful persistLaunchState / persistResumeState full-row write. A failed operation leaves the prior durable snapshot unchanged.
  • In apps/backend/internal/backendapp/orchestrator.go, adapt the GitHub service's non-secret policy descriptor into the executor interface alongside the existing lease adapter.

Managed runtime tools and CLI refresh

  • Refactor apps/backend/cmd/agentctl/github_cli_shim.go so the control process always creates one private managed GitHub tool directory containing both gh and agentctl links/copies, records its path, and does not globally prepend it for every instance.
  • In apps/backend/internal/agentctl/server/config/config.go, prepend that directory only when the instance's additional environment contains the complete broker contract. All agent processes, shells, background commands, and agentctl Git operations then share the same per-instance PATH.
  • Preserve runGitHubCLIShim lookup of the real gh outside the managed directory. Executor inheritance and profile-token override instances must see the normal executor PATH.
  • Add a real subprocess regression around the standalone shape: control agentctl starts without broker env, a broker-enabled instance receives it later, and both git credential fill and gh resolve through the managed tools. Missing broker/helper fails instead of consulting an ambient helper.
  • In apps/backend/internal/github/auth_resolver.go, give named-CLI cached credentials a separate five-minute cache deadline without pretending the provider token itself has an expiry. Generation invalidation remains immediate.

Frontend

Task Git credentials settings

  • Extend GitHubWorkspaceSettings and UpdateGitHubWorkspaceSettingsRequest in apps/web/lib/types/github.ts.
  • Add apps/web/components/github/github-task-credentials-section.tsx, registered with useSettingsSaveContributor, with two explicit choices: Managed workspace credentials and Inherit executor Git credentials.
  • Visible copy must explain local/Worktree host behavior, remote executor behavior, profile-token precedence, and when each choice applies. Reuse the repository-scope responsive help pattern: tooltip/focus on a fine pointer and a 44px information-button Drawer on a coarse pointer.
  • Update github-auth-method-list.tsx, github-connection-dialog.tsx, and github-settings.tsx so PAT, named CLI, and App cards state where their credential is stored/resolved and how managed tasks receive it. The information disclosure supplements rather than replaces this text.

Changes branch credential disclosure

  • Add a pure parser/view-model module beside the Changes panel that reads git_credential_snapshot from the active TaskSession.metadata, tolerates malformed/legacy values, and produces display labels without guessing unknown actors.
  • Extract the current branch-card body from apps/web/components/task/changes-panel-header.tsx. Keep the compact fine-pointer trigger, but make it keyboard-focus accessible and include policy, effective method/source, actor, and transport below the existing branch/base-branch rows.
  • Use useTouchDrawer for coarse pointers. The mobile trigger is at least 44px and opens a Drawer containing the same branch editing/base-branch controls and credential truth; desktop keeps the hover/focus card.
  • Select the active session snapshot in useChangesPanelData and pass the normalized disclosure through both desktop changes-panel.tsx and mobile/mobile-changes-panel.tsx.

Tests

  • What: fresh/replayed schemas default existing and new workspaces to managed; policy round-trips, partial updates reject invalid values, and settings copy includes policy but not authentication.
    File: apps/backend/internal/github/workspace_settings_test.go, copy_test.go, and focused migration tests.
    How: real SQLite store/service tests, including same-database schema replay.
  • What: the internal policy description identifies PAT/CLI humans and App registration context without resolving tokens, while executor actors remain unknown.
    File: focused tests under apps/backend/internal/github/.
    How: table-driven service tests with fake connections/registration store.
  • What: initial launch and resume produce the same managed/executor/profile-override contract; managed helper chains reset, inheritance injects nothing, and only successful lifecycle launches persist a new session snapshot.
    File: apps/backend/internal/orchestrator/executor/executor_credentials_test.go, executor_resume_test.go, and launch-path tests.
    How: table-driven unit tests plus mocked lifecycle success/failure integration tests.
  • What: host/executor/profile/task indexed Git config blocks compose in precedence order, already-forwarded suffixes occur once, malformed blocks fail clearly, and unrelated settings remain active beside Kandev's managed helper.
    File: new shared utility tests, apps/backend/internal/agentctl/server/config/config_test.go, apps/backend/internal/agent/runtime/lifecycle/executor_docker_test.go, and focused remote tests.
    How: table-driven block composition plus a temp-repository real Git commit/hook subprocess regression using locstat-shaped entries.
  • What: standalone per-instance broker activation makes agentctl and gh discoverable even though the control process started without broker env; inherit/override instances do not activate the tool directory.
    File: apps/backend/cmd/agentctl/github_cli_shim_test.go, apps/backend/internal/agentctl/server/config/config_test.go.
    How: temp executable/subprocess and fake HTTP broker tests; invoke real Git credential plumbing where supported.
  • What: named CLI cache entries re-resolve after five minutes and still invalidate immediately on generation changes.
    File: apps/backend/internal/github/auth_resolver_test.go.
    How: deterministic fake clock and call counter.
  • What: settings drafts load, save, discard, and explain both policies and every automation method.
    File: new component/model tests beside apps/web/components/github/github-task-credentials-section.tsx.
    How: mocked API promises and settings-save provider; keyboard-focus help assertion.
  • What: valid/malformed/missing session snapshots produce truthful disclosure labels, never an inferred actor.
    File: new Changes credential view-model test and focused header component test.
    How: table-driven Vitest plus keyboard-focus component test under TooltipProvider.

Targeted pre-PR commands are recorded in the task files.


E2E Tests

  • Scenario: GIVEN workspace settings, WHEN the user switches to executor inheritance and saves, THEN the API persists executor; discard/reload and switching back to managed remain coherent, and the desktop help names PAT/CLI/App delivery and profile-token precedence.
    File: extend apps/web/e2e/tests/integrations/github-workspace-settings.spec.ts.
  • Scenario: GIVEN a coarse-pointer settings viewport, WHEN the task-credential information icon is tapped, THEN a Drawer contains the same delivery explanation, controls meet 44px targets, and the page has no horizontal overflow.
    File: extend apps/web/e2e/tests/integrations/mobile-github-auth-settings.spec.ts.
  • Scenario: GIVEN a session seeded with a managed CLI or App snapshot, WHEN a desktop user hovers/focuses the Changes branch trigger, THEN branch/base information and launch-time actor, method, and managed transport are visible.
    File: apps/web/e2e/tests/git/git-credential-identity.spec.ts.
  • Scenario: GIVEN an inherited or profile-token snapshot on a coarse-pointer task view, WHEN the branch trigger is tapped, THEN a Drawer labels the actor runtime-selected, has a 44px trigger, and does not overflow.
    File: apps/web/e2e/tests/git/mobile-git-credential-identity.spec.ts.

Production E2E runs only after rebuilding backend and Vite artifacts.


Public Documentation

  • Update docs/public/integrations.md, docs/public/executors.md, and docs/public/use-kandev.md to separate workspace automation method from task credential policy, describe named CLI brokerage, explain executor inheritance, and document the Changes snapshot.
  • Update troubleshooting so agentctl: command not found is a managed-runtime defect rather than a prompt to fall back to personal SSH, and distinguish managed-helper failure from missing executor credentials.

Implementation Waves And Parallel Candidates

Wave 1:

Wave 2 (parallel candidates — user authorization required):

Wave 3 (parallel candidates — user authorization required):

Wave 4:

The default execution is sequential in this primary conversation. Waves identify dependency-safe opportunities only and do not authorize subagents.

Risks

  • Local and Worktree agents retain host process authority. Managed routing can fail closed for Kandev-injected GitHub HTTPS/gh, but cannot prevent a capable agent from selecting SSH or another host tool.
  • The helper-reset entry must be ordered before Kandev's helper without deleting unrelated caller-supplied Git config.
  • Indexed Git config keys form one structured protocol. Plain map overlay at any launch boundary can regress host tooling; arbitrary de-duplication can also change the meaning of multi-valued Git settings.
  • Docker and remote control processes can already contain the task suffix before it is forwarded to an instance, so composition must remove only an exact boundary overlap.
  • Resume must resolve every attached repository, not only the legacy primary fields.
  • The snapshot is launch-time truth. Rendering current workspace status in its place would reintroduce misleading identity.
  • Agentctl tool-directory activation must be per instance so executor inheritance and explicit profile-token overrides do not accidentally invoke the broker shim.