4.2 KiB
4.2 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/create-local-repository/spec.md | 2026-07-30 | completed |
Implementation Plan: Descriptor-Bound Local Repository Initialization
Overview
git init /dev/fd/3 cannot enter an inherited directory descriptor on macOS because fdescfs
presents the descriptor without directory search permission. PR #2052 avoids that failure by
setting exec.Cmd.Dir to the staging pathname, but doing so lets a pathname replacement redirect
Git before the post-initialization identity check. Replace the platform-specific descriptor path
with a small inherited-fd helper process so Linux and macOS initialize the verified directory
identity without changing the backend process's working directory.
Backend
Inherited-descriptor Git helper
- Add
apps/backend/internal/task/gitinit/with a narrow command boundary for local repository initialization. - On Linux and macOS, start the current executable with a private helper argument, an explicit
internal environment marker, and the verified directory inherited as fd 3. Package initialization
intercepts only that marked subprocess before any application or test entry point. The helper
calls
fchdir(3)and replaces itself withgit init --initial-branch=main, preserving context cancellation because Git keeps the helper process identity. - Windows retains its existing pathname/current-directory behavior. Other compiled Unix targets keep a compatibility fallback, but remain outside the supported product scope because they do not implement the required ownership and exclusive-publication guarantees.
- Keep the helper out of public
cmd/kandevdispatch and CLI help.
Task service
- Update
initializeGitRepositoryinapps/backend/internal/task/service/local_repository_initialization.goto request the command from the helper boundary and remove the/proc/self/fdversus/dev/fdruntime path selection. - Preserve combined-output error reporting, unborn
main, cancellation, post-initialization identity validation, exclusive publication, and fail-closed cleanup. - Correct implementation comments so they describe supported Linux/macOS behavior without claiming that the user-facing feature works on BSD.
Tests
- Descriptor identity after pathname replacement
- Files:
apps/backend/internal/task/gitinit/command_descriptor_test.go,apps/backend/internal/task/service/local_repository_initialization_descriptor_test.go - How: open staging directory A, rename it, install replacement B at the old pathname, invoke
the real helper subprocess, and assert
.gitis created only in A while B remains empty.
- Files:
- Helper dispatch and failure propagation
- Files:
apps/backend/internal/task/gitinit/*_test.go,apps/backend/internal/task/handlers/repository_handlers_test.go - How: use a helper-process test to exercise inherited fd 3, verify hidden dispatch, and assert
invalid descriptors or missing Git return a non-zero result with useful stderr. Exercise the
HTTP handler from its own test binary to prove dispatch is not tied to a package-specific
TestMain.
- Files:
- Existing initialization behavior
- File:
apps/backend/internal/task/service/local_repository_initialization_test.go - How: retain real-Git coverage for the commitless unborn
mainrepository, persistence rollback, and pathname canonicalization.
- File:
Implementation Waves
Wave 1:
- task-01-descriptor-bound-git-init - completed
The repair is one sequential task because the helper command, process-init trampoline, service wiring, and identity regression test form one process boundary and touch shared behavior.
Risks
- The helper must never change the long-running backend's process-wide working directory.
- The helper must activate only for a subprocess carrying both the private argument and internal environment marker, regardless of which application or test binary links the package.
fchdirand descriptor inheritance are supported on Linux and macOS; Windows must keep its existing non-ExtraFilespath.- A replaced request-owned inode may be impossible to clean up after an attacker moves it, but the replacement path must remain untouched and no repository row may be persisted.