Files
kandev/docs/plans/task-owned-worktree-lifecycle/task-01-task-worktree-persistence.md
T

133 lines
6.7 KiB
Markdown

---
id: "01-task-worktree-persistence"
title: "Normalize task-environment worktree ownership"
status: done
wave: 1
depends_on: []
plan: "plan.md"
spec: "../../specs/session-delete-resource-cleanup/spec.md"
decision: "../../decisions/2026-08-08-task-owned-worktree-lifetime.md"
---
# Task 01: Normalize Task-Environment Worktree Ownership
## Acceptance
- `task_environment_repos` is the only physical-worktree record and is queryable
by task through its owning `task_environments` row without a live session.
- `task_sessions.task_environment_id` is the only session/workspace association.
- `task_session_worktrees`, its indexes, Go model, repository methods, duplicate
schema initializer, and runtime readers/writers are removed.
- Deprecated flat `repository_id`, `worktree_id`, `worktree_path`, and
`worktree_branch` fields are removed from the `task_environments` schema,
models, persistence methods, and API.
- Initial, resumed/recreated, multi-repository, and mid-session materialization
paths persist environment repository ownership before exposing the worktree
for reuse.
- Environment ownership, repository worktrees, and the session environment link
are committed consistently.
- Rejected persistence compensates only a worktree created by that attempt.
- SQLite and PostgreSQL upgrades normalize legacy environment and session data,
validate it, and remove obsolete schema in one rollback-safe migration.
- The cutover uses a dedicated error-returning migration under a database writer
or advisory lock; it never uses a helper that swallows unexpected errors.
- The complete legacy and final worktree identity/path/branch inventories match
before the schema swap commits.
- Fresh databases contain only the final schema; production runtime code has no
legacy read, write, or fallback path.
- Preview-build `session_delete` cleanup jobs are removed before worker startup;
task-lifecycle cleanup jobs are preserved.
- Conflicting legacy identities or paths fail migration with enough detail to
repair the data, leaving the pre-upgrade database intact.
- Injected failure at every copy, validation, drop, rename, and commit boundary
rolls back to the complete old schema and data.
- PostgreSQL advisory and affected-table locks reject concurrent migration or
mixed-version writes during cutover; lock timeout changes nothing.
- The migration performs no filesystem or Git operations, and cleanup workers
cannot start against a partially initialized repository.
- SQLite snapshot restore and PostgreSQL operator-backup restore are verified as
the downgrade path; old binaries are never run against the final schema.
- Task inventory and active-worktree-path reads no longer require a session join.
## TDD Sequence
1. RED: add SQLite and PostgreSQL fresh-schema tests for the exact final
environment/repository/session shape and absence of obsolete tables/columns.
2. RED: seed a task environment, a legacy flat environment, and a session-only
association; prove all three normalize into environment repository ownership,
sessions receive the correct environment ID, and session deletion preserves
the owner. Add conflicting-source cases that roll back the migration.
3. RED: assert successful upgrade drops `task_session_worktrees` and deprecated
environment columns for both database engines, including migration replay.
4. RED: inject errors after each shadow-copy, validation, drop, rename, and
pre-commit step; reopen the database and prove its legacy schema and data are
byte-for-byte equivalent at the ownership-field level.
5. RED: exercise concurrent PostgreSQL initializers and prove the advisory lock
and affected-table locks serialize the cutover and reject mixed-version
writes. Exercise SQLite writer contention and prove timeout or failure leaves
the database unchanged.
6. RED: add store tests for task and session projections through the environment,
zero-session inventory, ownership transfer, and atomic persistence.
7. GREEN: implement the schema normalization and remove legacy models, CRUD,
API fields, schema initialization, and dual-write paths.
8. GREEN: wire every physical materialization path and bounded compensation.
9. REFACTOR: make `task_environment_repos` the shared projection for branch
updates, session responses, storage inventory, and worktree management.
10. REFACTOR: add an architecture assertion that legacy ownership symbols are
confined to the migration and its fixtures.
## Verification
```bash
cd apps/backend && go test ./internal/task/repository/sqlite/... ./internal/worktree/...
cd apps/backend && go test ./internal/backendapp/... -run 'Worktree|BranchMaterial'
```
Run PostgreSQL schema/migration tests with the repository's configured test DSN;
recording only a skipped result is incomplete.
Also run the persistence provider upgrade tests to prove SQLite backup failure
prevents migration and successful upgrade leaves a restorable snapshot.
## Files likely touched
- `apps/backend/internal/task/repository/sqlite/base_schema.go`
- `apps/backend/internal/task/repository/sqlite/base_migrations.go`
- `apps/backend/internal/task/repository/sqlite/postgres_schema_test.go`
- `apps/backend/internal/task/repository/sqlite/schema_replay_test.go`
- `apps/backend/internal/persistence/upgrade_test.go`
- `apps/backend/internal/persistence/snapshot_test.go`
- `apps/backend/internal/task/models/models.go`
- `apps/backend/internal/worktree/store.go`
- `apps/backend/internal/worktree/manager.go`
- `apps/backend/internal/worktree/manager_lifecycle.go`
- `apps/backend/internal/backendapp/branch_materializer.go`
- `apps/backend/internal/task/repository/interface.go`
- `apps/backend/internal/task/repository/sqlite/task_environment.go`
- `apps/backend/internal/task/repository/sqlite/session.go`
- `apps/backend/internal/orchestrator/service.go`
- `apps/backend/internal/orchestrator/executor/executor.go`
- `apps/web/lib/api/domains/task-environment-api.ts`
- task-environment API consumers that use deprecated flat worktree fields
## Dependencies
None.
## Inputs
- `docs/specs/tasks/runtime-cleanup.md`, especially the normalized task
environment data model.
- `docs/decisions/2026-08-08-task-owned-worktree-lifetime.md`.
- Existing `TaskEnvironment` ownership-transfer behavior and worktree manager
materialization rollback patterns.
## Output contract
Report the exact final schema, removed tables/columns/types/methods, backfill
grouping and source precedence, old/new inventory comparison, conflict
diagnostics, every injected rollback result, SQLite snapshot evidence,
PostgreSQL advisory-lock evidence, every materialization path audited,
compensation behavior, backup-restore/downgrade evidence, and SQLite/PostgreSQL
results. No legacy reader or writer may be deferred.