fix: prevent permanent owned-link target mismatch on task launch (#2253)
This commit is contained in:
@@ -38,7 +38,9 @@ manually moving files into the task workspace.
|
||||
- A source file rename may stay within its canonical workspace/source root; a cross-root move or
|
||||
rename is rejected before either source is mutated.
|
||||
- A multi-source submission is atomic from the user's perspective: either every source is attached
|
||||
and materialized in the current task environment, or none of the new attachments remain.
|
||||
and materialized in the current task environment, or none of the new attachments remain. When an
|
||||
attachment repointed a pre-existing Kandev-owned entry, a failed submission restores that entry to
|
||||
the target it had before the attachment rather than deleting it.
|
||||
- Repository attachment works for every executor that can run the task. Arbitrary folders are
|
||||
available only to Local and Worktree tasks, where the selected host paths remain live. Container
|
||||
and remote pickers do not offer the folder source kind, and the backend rejects a forged folder
|
||||
@@ -192,6 +194,10 @@ persisted in source URLs or copied into agent-visible metadata.
|
||||
| A requested file move or rename crosses canonical source roots | The request is rejected before either source is mutated. |
|
||||
| A persisted local folder later disappears | The current live environment keeps its existing materialization; a new/reset environment surfaces the missing source and does not silently omit it. |
|
||||
| The client disconnects during materialization | Rollback runs on a detached bounded context and the eventual task event reflects durable state. |
|
||||
| A Kandev-owned task-root entry already points at a different directory than the current durable spec (e.g. a stale entry left by an earlier launch) | Because the task root is Kandev-owned and the entry is a directory link, reconciliation repoints it to the current spec target and the launch/resume proceeds; it does not fail closed forever. |
|
||||
| A Kandev-owned task-root entry's ownership marker names a different task than the one reconciling | Reconciliation does not repoint the entry; it fails closed with a marker-conflict error and leaves the other task's entry pointing at its existing target. |
|
||||
| A multi-source attachment that repointed a pre-existing owned entry later fails during materialization | Rollback restores each repointed entry to the target it had before the attachment and deletes only entries this attachment created; no pre-existing entry is left deleted. |
|
||||
| A safe replacement would overwrite a non-owned or out-of-root entry, or its recreate fails after removal | A non-owned or changed entry, or a traversal path, is rejected before any removal, and a failed recreate restores the entry to its prior target rather than leaving it missing. |
|
||||
|
||||
## Persistence guarantees
|
||||
|
||||
@@ -201,6 +207,14 @@ checkouts from durable repository attachments; they never persist folder attachm
|
||||
conversations and source records survive an environment restart even when runtime materialization
|
||||
must be retried.
|
||||
|
||||
Each task that materializes a Kandev-owned task root under the tasks base directory derives its
|
||||
task-root directory name from task identity. The name is collision-resistant, not injective: two
|
||||
distinct tasks — including two tasks whose titles sanitize to the same slug, or a local task and a
|
||||
worktree task sharing a title — are overwhelmingly unlikely to resolve to the same task root, and any
|
||||
residual collision is caught by the fail-closed ownership marker, which verifies task identity before
|
||||
any Kandev-owned entry under a shared root is repointed. The task-root name is computed once and
|
||||
persisted; every relaunch and resume of that task reuses the persisted name.
|
||||
|
||||
## Scenarios
|
||||
|
||||
- **GIVEN** a repository-backed task with the Files tree loaded, **WHEN** the user opens the
|
||||
@@ -278,6 +292,29 @@ must be retried.
|
||||
horizontal document overflow and returns focus to the workspace-actions control.
|
||||
- **GIVEN** an agent calls `add_workspace_sources_kandev` for its current idle task, **WHEN** all
|
||||
inputs materialize, **THEN** the UI receives the same task and session updates as the human flow.
|
||||
- **GIVEN** two distinct tasks whose titles sanitize to the same task-root slug, **WHEN** each task
|
||||
materializes a Kandev-owned task root, **THEN** their collision-resistant suffixes normally produce
|
||||
different task-root directory names; if a residual suffix collision occurs, the ownership marker
|
||||
rejects cross-task repointing with a marker-conflict error rather than redirecting the other task's
|
||||
entries.
|
||||
- **GIVEN** a local task whose persisted task root already contains a Kandev-owned directory-link
|
||||
entry for a repository that points at a different directory than the current durable spec target,
|
||||
**WHEN** the task launches or resumes, **THEN** reconciliation repoints the owned entry to the
|
||||
current spec target and the launch/resume proceeds instead of failing with an owned-link target
|
||||
mismatch on every attempt.
|
||||
- **GIVEN** a Kandev-owned task-root entry that is not a directory link (a real file or directory a
|
||||
reconcile did not create), **WHEN** the task launches or resumes, **THEN** reconciliation does not
|
||||
delete or overwrite it and the launch surfaces an error identifying the conflicting entry.
|
||||
- **GIVEN** two persisted legacy tasks whose environments share one Kandev-owned task root, **WHEN**
|
||||
the second task launches or resumes and its ownership marker does not match the root's marker,
|
||||
**THEN** reconciliation fails closed with a marker-conflict error and does not redirect the first
|
||||
task's live entry.
|
||||
- **GIVEN** a multi-source attachment that repointed a pre-existing Kandev-owned entry, **WHEN** a
|
||||
later source in the same submission fails to materialize, **THEN** rollback restores the repointed
|
||||
entry to its prior target and no pre-existing entry is left deleted.
|
||||
- **GIVEN** a Kandev-owned entry whose recreate could fail after the old link is removed, **WHEN**
|
||||
reconciliation replaces it, **THEN** a traversal or out-of-root name is rejected before any removal
|
||||
and a failed recreate leaves the original entry intact rather than missing.
|
||||
|
||||
## Out of scope
|
||||
|
||||
@@ -300,4 +337,5 @@ must be retried.
|
||||
|
||||
See [Attach Workspace Sources plan](../../plans/attach-workspace-sources/plan.md) and the
|
||||
[live add-branch compatibility repair plan](../../plans/restore-live-add-branch/plan.md), plus the
|
||||
[multi-repository chat file-link repair plan](../../plans/multi-repo-chat-file-links/plan.md).
|
||||
[multi-repository chat file-link repair plan](../../plans/multi-repo-chat-file-links/plan.md) and the
|
||||
[owned link target mismatch repair plan](../../plans/owned-link-target-mismatch-repair/plan.md).
|
||||
|
||||
Reference in New Issue
Block a user