fd79d6170f
* feat(tasks): multi-branch tasks — N (repo, branch) pairs per task Extends multi-repo into multi-branch so a single task can hold multiple worktrees on the same repo (one per branch). Sibling worktrees layout at <task>/<repo>/ + <task>/<repo>-<slug>/, dedicated PRWatch per branch, live agentctl tracker rescan + frontend store updates so the UI splits PRs / files / commits per branch without a session restart. MCP add_branch_to_task_kandev tool is shielded to the worktree executor. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(web): unify multi-branch repo label as <repo> · <branch> across panels PR Changes and Commits panels rendered repo labels differently for multi-branch tasks — Commits showed "kandev · feature/x" while PR Changes leaked the raw "kandev-feature-x" agentctl tracker tag. Reformat tracker-tagged repo names via formatRepoLabel using the longest known-repo prefix so kandev-cli-* doesn't collapse to kandev · cli-*. * fix(tasks): address multi-branch PR review — data races, slug collisions, MCP error codes Backend - agentctl process.Manager: add repoTrackersMu RWMutex guarding repoTrackers + workspaceTracker. Reads snapshot under RLock; RescanRepositories builds trackers first, takes Lock for swap/append. Validates newWorkDir via os.Stat before publishing cfg.WorkDir. - worktree.Manager: reject invalid explicit BranchSlug up-front; cache key uses sanitized wt.BranchSlug so read/write paths are symmetric. - worktree.store: narrow isDuplicateColumnError to "duplicate column name". - task service AddBranchToTask: slug-collision guard rejects feat/a vs feat-a. - orchestrator resolvePushRepo: two-pass exact-name match before subdir prefix fallback so "backend" doesn't swallow pushes for "backend-admin". - executor buildRepoSpecs: duplicate repos always get a non-empty BranchSlug (falls back to branch-N when both branch fields sanitize to empty). - MCP handler: classifyAddBranchError maps service errors to NOT_FOUND / CONFLICT / VALIDATION_ERROR; add ErrorCodeConflict constant. - MCP server: extract registerAddBranchToTaskTool, register in task mode only (external mode contract: config tools + create_task_kandev). - workspace_rescan handler: reject malformed JSON with 400 instead of silently rescanning the current root. - branch_materializer: split MaterializeBranch into prepare + finalize helpers to satisfy the 80-line cap. Frontend - existingPrUrl returns undefined when a task has >1 PR so per-repo View PR buttons take over (no silent linking to a sibling). - formatRepoLabel checks exact known-repo match before prefix splitting so "kandev-cli" isn't mislabeled as "kandev · cli". - Repo chip key now includes base_branch so siblings differing only on base render as distinct chips. Docs - multi-branch spec uniqueness wording aligned with the canonical 4-column key (task_id, repository_id, base_branch, checkout_branch). - worktree.CreateRequest.BranchSlug doc describes the sibling layout (<repo>-<slug>) instead of the obsolete nested form. * fix(tasks): scope slug-collision check to repo only + use errors.Is for ErrTaskNotFound - service_branches: drop base_branch from the slug-collision check. Worktree path is (repo, slug); two rows on the same repo cut from different bases still collide on disk. - service_branches: prepareBranchAdd wraps taskrepo.ErrTaskNotFound via fmt.Errorf with %w so classifiers don't have to substring-match the formatted task UUID. - mcp handlers: classifyAddBranchError uses errors.Is(err, ErrTaskNotFound). * fix(agentctl): sanitize rescan work_dir before os.Stat to clear CodeQL path-injection CodeQL flagged the rescan endpoint as a path-injection sink (the HTTP-supplied work_dir flowed straight into os.Stat and downstream scanRepositorySubdirs). Add validateRescanPath which: - requires the input to be a clean absolute path with no ".." segments - rejects anything that isn't equal to or an ancestor of the current cfg.WorkDir — the only legitimate use case is the branch materializer promoting <task>/<repo>/ to <task>/ Defense in depth: agentctl binds to localhost, but any colocated process could otherwise direct it at /etc or the user's home by POSTing /api/v1/workspace/rescan. * fix(agentctl): tighten validateRescanPath, close TOCTOU on existingTrackers, add tests - Drop the strings.Contains(clean, "..") guard. filepath.Clean already resolves traversal; the substring check rejects legitimate names like /home/u/..hidden without adding defense (the ancestor check via filepath.Rel is the real boundary). - Drop the rel == "." branch in the ancestor check — early return for clean == currentClean already covers identity. - Read existingTrackers inside the write-lock section (not under RLock then re-Lock) so two concurrent rescans can't both see 0 trackers and double-bootstrap the multi-repo set. - Add table-driven TestValidateRescanPath covering rejection paths (relative, descendant, sibling, traversal-cleaned, empty input), legitimate ancestor promotion, exact match, no-anchor first-launch, and a /home/u/..hidden dotted-segment case. * fix(agentctl): break taint flow into os.Stat — derive rescan path from cfg, not HTTP body CodeQL's go/path-injection trace still flagged manager_rescan.go after the prior sanitizer because the user-supplied newWorkDir flowed (after validation) into os.Stat and then into cfg.WorkDir → tracker paths → workspace_monitor os.Stat sinks. Replace validateRescanPath with resolveRescanPath: it returns a path DERIVED from currentWorkDir (no-op = currentClean, promotion = filepath.Dir(currentClean)) and uses newPath only for comparison, never as a source for the returned string. os.Stat now receives a path whose provenance is trusted manager config. Functionally the legitimate caller (branch materializer promoting <task>/<repo>/ to <task>/) is unchanged. * test(agentctl): make TestResolveRescanPath windows-portable The Linux Backend Tests job passed but the Backend (windows) job rejected every "absolute" case because filepath.IsAbs("/task/repo") is false on Windows (no volume letter). Build inputs via filepath.Join against absRoot() (filepath.Abs of "/" / "\\") so the same table covers POSIX (`/...`) and Windows (`C:\...`) without per-OS branches. Keep the traversal-after-Clean case POSIX-only since filepath.Clean is the shared primitive and that input form needs a volume to be considered absolute on Windows anyway. * fix(agentctl): serialize RescanRepositories + re-check trackers under write lock Two concurrent RescanRepositories calls could both observe an empty tracker set (between the snapshot read and the bootstrap branch) and both enter transitionToMultiRepoMode, leaking a duplicate bare-root tracker and duplicate per-repo trackers. Same race in appendNewRepoTrackers would double-append the same new child across both callers. Fix: - Add rescanMu sync.Mutex on Manager. Acquired for the full body of RescanRepositories so the snapshot → branch → mutate sequence is serialized. repoTrackersMu still guards individual field reads from unrelated goroutines (subscribe/unsubscribe/poll-mode). - appendNewRepoTrackers re-checks membership inside the write-lock before appending and drops already-present trackers (Stop + detach outside the lock). Defense in depth. - Test: add `dotted segment is legal as promotion` case so the ..hidden-segment promotion path stays exercised. --------- Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
126 lines
5.0 KiB
Markdown
126 lines
5.0 KiB
Markdown
# Feature Specs
|
|
|
|
Specs for kandev product features, grouped by umbrella. Each spec describes a user-invocable capability and is the source of truth for "is this feature done?"
|
|
|
|
The bar: an agent given only a spec (no source code) should be able to either reimplement the feature or test the existing system for conformance. See `.agents/skills/spec/SKILL.md` for the workflow and template.
|
|
|
|
**Status:** `draft` (being written) · `building` (in active development) · `shipped` (implemented, spec matches code) · `archived` (deprecated).
|
|
|
|
**`needs-upgrade`** in a spec's frontmatter flags template sections that the original sources did not cover and should be filled in from code (Data model, API surface, State machine, Permissions, Failure modes, Persistence guarantees). All office specs have been brought to the implementability bar; this flag is only used for newly-drafted specs that need a code-driven fill-in pass.
|
|
|
|
---
|
|
|
|
## office/ — autonomous agent management
|
|
|
|
The office umbrella covers kandev's autonomous-agent product surface: workspaces of long-running agents that pick up tasks, coordinate via handoffs, and report through a dashboard.
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [overview](office/overview.md) | draft |
|
|
| [agents](office/agents.md) | draft |
|
|
| [tasks](office/tasks.md) | draft |
|
|
| [scheduler](office/scheduler.md) | draft |
|
|
| [runtime](office/runtime.md) | draft |
|
|
| [routing](office/routing.md) | draft |
|
|
| [costs](office/costs.md) | in-progress |
|
|
| [dashboard](office/dashboard.md) | draft |
|
|
| [live-updates](office/live-updates.md) | draft |
|
|
| [inbox](office/inbox.md) | draft |
|
|
| [assistant](office/assistant.md) | draft |
|
|
| [plugins](office/plugins.md) | draft |
|
|
| [automations-settings](office/automations-settings.md) | draft |
|
|
| [testing](office/testing.md) | shipped |
|
|
|
|
## tasks/ — task & workflow model
|
|
|
|
Kandev's task model: documents, execution stages, labels, blocker escalation, subtask checklists, subtree controls, and the unification with the workflow engine.
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [documents](tasks/documents.md) | shipped |
|
|
| [execution-stages](tasks/execution-stages.md) | shipped |
|
|
| [labels](tasks/labels.md) | shipped |
|
|
| [model-unification](tasks/model-unification.md) | draft |
|
|
| [without-repositories](tasks/without-repositories.md) | draft |
|
|
| [subtask-checklist](tasks/subtask-checklist.md) | shipped |
|
|
| [subtree-controls](tasks/subtree-controls.md) | shipped |
|
|
| [blocked-task-escalation](tasks/blocked-task-escalation.md) | draft |
|
|
| [multi-branch](tasks/multi-branch/spec.md) | shipped |
|
|
|
|
## agents/ — agent governance
|
|
|
|
Roles, governance gates, and granular permissions that apply across human users and office agents.
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [roles](agents/roles.md) | shipped |
|
|
| [governance](agents/governance.md) | shipped |
|
|
| [granular-permissions](agents/granular-permissions.md) | draft |
|
|
|
|
## integrations/ — external service integrations
|
|
|
|
Per-workspace credentials and triage triggers for external services.
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [slack](integrations/slack.md) | shipped |
|
|
| [external-mcp](integrations/external-mcp.md) | draft |
|
|
| [gitlab-integration](gitlab-integration/spec.md) | shipped |
|
|
|
|
## workspaces/ — workspace lifecycle
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [deletion](workspaces/deletion.md) | shipped |
|
|
|
|
## costs/ — cost tracking & budgets
|
|
|
|
Subscription quota tracking and per-agent cheap-model profile routing.
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [subscription-usage](costs/subscription-usage.md) | draft |
|
|
| [cheap-model-profiles](costs/cheap-model-profiles.md) | shipped |
|
|
|
|
## ui/ — cross-cutting UI features
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [comment-markdown](ui/comment-markdown.md) | shipped |
|
|
| [empty-turn-notice](ui/empty-turn-notice.md) | shipped |
|
|
|
|
## system-page/ — operational diagnostics & maintenance UI
|
|
|
|
System pages (Radarr/Sonarr-style) for status, disk usage, database maintenance, backups, logs, updates, OSS licenses, and about.
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [system-page](system-page/spec.md) | draft |
|
|
|
|
---
|
|
|
|
## Standalone
|
|
|
|
| Spec | Status |
|
|
|---|---|
|
|
| [improve-kandev](improve-kandev/spec.md) | draft |
|
|
| [homebrew-core](homebrew-core/spec.md) | draft |
|
|
| [public-share-links](public-share-links/spec.md) | draft |
|
|
| [ssh-executor](ssh-executor/spec.md) | draft |
|
|
| [cli-mode-parity](cli-mode-parity/spec.md) | draft |
|
|
|
|
---
|
|
|
|
## Conventions
|
|
|
|
- **Spec layout.** Umbrella specs live as flat `.md` files under the umbrella directory (`docs/specs/office/agents.md`). Standalone specs use a folder (`docs/specs/improve-kandev/spec.md`).
|
|
- **Plans are not specs.** Implementation plans (`plan.md`) are working files, gitignored. Specs are the durable requirements.
|
|
- **Bug fixes are not specs.** Bugs produce a regression test plus an ADR if they encoded a new convention. See `/fix` skill.
|
|
- **Architecture decisions are not specs.** ADRs live under `docs/decisions/`. See `/record decision`.
|
|
|
|
## Cross-references
|
|
|
|
- ADRs: [`../decisions/INDEX.md`](../decisions/INDEX.md)
|
|
- Spec workflow: [`.agents/skills/spec/SKILL.md`](../../.agents/skills/spec/SKILL.md)
|
|
- Bug-fix workflow: [`.agents/skills/fix/SKILL.md`](../../.agents/skills/fix/SKILL.md)
|