تصور کنید یک عامل هوش مصنوعی در حال مدیریت دهها شاخه کد در محیطهای مجزا است، اما ناگهان دیسک سرور شما بدون دلیل پر میشود. اگر از Git worktrees برای ایزولهسازی تسکهای عاملهای خود استفاده میکنید، احتمالاً با «فایلهای شبح» روبرو هستید که ابزارهای استاندارد گیت قادر به شناسایی آنها نیستند. این چالش اصلی سازنده VADD است؛ یک داشبورد محلی که محیطهای Claude Code و Codex را مدیریت میکند. او شش نقطه شکست بحرانی را شناسایی کرده است که زمانی رخ میدهند که عاملهای هوش مصنوعی خودشان مدیریت چکاوتها (checkouts) را بر عهده میگیرند.
در ساختار VADD، هر هدف (Objective) در یک worktree مجزا در مسیر ~/.vadd/worktrees/<projectId>/<objectiveId> اجرا میشود. اکثر توسعهدهندگان از worktreeها استفاده میکنند تا به هر تسک AI یک شاخه اختصاصی بدهند بدون اینکه چکاوت اصلی را به هم بریزند. این یک راهکار بومی و ارزان است که در ظاهر بینقص به نظر میرسد، اما وقتی اتوماتیک میشود، مشکلاتی ظاهر میگردد. این رویکرد در واقع پاسخی به چالشهای تداخل در کدنویسی AI است که در روشهای سنتی مدیریت شاخهها دیده میشد. مسئله این است که «موفقیت در سطح گیت» و «موفقیت در سطح سیستم فایل» دو حقیقت متفاوت هستند.
برای درک بهتر، پروژهای با فریمورک Laravel را تصور کنید. عامل یک نصب کانتینری را اجرا میکند که فایلها را با دسترسی root مینویسد. وقتی عامل سعی میکند worktree را حذف کند، گیت لینک مدیریتی را حذف میکند اما در حذف فایلهای متعلق به root شکست میخورد. سیستم گزارش موفقیت میدهد، اما دیسک همچنان با دایرکتوریهای «شبح» پر میشود.
نشت دایرکتوریهای شبح
طبق مستندات VADD، دستور worktree remove --force ابتدا لینک .git را حذف میکند. اگر حذف بازگشتی (recursive delete) متعاقب آن شکست بخورد — که اغلب به دلیل وجود پوشههای vendor/ یا node_modules/ با مالکیت root است — گیت پیش از آنکه متوجه شکست شود، وجود آن worktree را فراموش کرده است. این رفتار مشابه برخی موانع حذف خودکار کدها در Claude Code است که به دلیل قفلهای سیستمی رخ میدهد.
در یک مورد واقعی، یک اجرای پاکسازی گزارش موفقیت ۲۶ مورد از ۲۶ مورد را نشان میداد، اما ۱.۷ گیگابایت داده در پنج دایرکتوری باقی مانده بود. دلیل این اتفاق این بود که کاربر نمیتوانست فایلهایی را در دایرکتوری حذف کند که مالک آنها نبود (به طور مشخص backend/vendor که با مالکیت root:root نوشته شده بود).
- دستور
git worktree pruneاین نشتها را پیدا نمیکند چون حذف شدن فایل لینک.gitدقیقاً همان چیزی است که باعث میشود گیت یک worktree را از ثبت خود خارج کند. - دستور
git worktree listنمیتواند این دایرکتوریها را نشان دهد چون گیت دیگر نمیداند آنها وجود دارند. - تنها راه حل این است که به صورت دستی دایرکتوری ریشه worktreeها را
readdirکنید و هر پوشهای را که توسط یک ردیف دیتابیس یا ورودی گیت ادعا نشده است، گزارش کنید.
برای جلوگیری از این وضعیت، اتوماسیون باید هم رجیستری گیت و هم سیستم فایل را بررسی کند:
۱. مسیر worktree را استخراج کند.
۲. بررسی کند که آیا listWorktrees هنوز هدف مورد نظر را شامل میشود یا خیر.
۳. از existsSync استفاده کند تا تایید کند دایرکتوری واقعاً از روی دیسک حذف شده است.

شکافهای محیطی و وابستگیها
یک worktree تازه، کاملاً خالی از فایلهای ردیابینشده (untracked) است. عاملی که سعی میکند یک گردش کار «ابتدا-تست» (test-first) را اجرا کند، بلافاصله شکست میخورد؛ زیرا فایلهای .env، پوشه node_modules و دایرکتوری vendor را ندارد و فقط به فایلهای ردیابیشده دسترسی دارد.
استفاده از Symlink برای node_modules ریشه در ساختارهای پیچیده مثل pnpm workspaces ناکافی است. از آنجایی که pnpm مسیرها را از طریق node_modules هر پکیج حل میکند، یک symlink ساده همچنان میتواند منجر به خطاهایی مانند Cannot find package 'zod' شود.
مکانیزمهای راهاندازی
راه حل، تعریف یک مرحله Setup برای هر مخزن در فایل .vadd/config.json است که دقیقاً یک بار هنگام ایجاد worktree اجرا شود. این مرحله باید پیش از اولین تسک تاییدیه اجرا شود، زیرا عاملها نیاز دارند بلافاصله تستها را اجرا کنند.
مثال از پیکربندی:
{
"verify": {
"setup": [{ "id": "deps", "run": "pnpm install --frozen-lockfile" }],
"commands": [{ "id": "test", "run": "pnpm test", "required": true }]
}
}
اگر این Setup شکست بخورد، هدف (Objective) باید در وضعیت setup_failed متوقف شود و لاگها به عنوان مدرک نگه داشته شوند، به جای اینکه اجازه دهیم عامل در worktreeای که قابلیت بیلد شدن ندارد، دست و پا بزند.
توهم لینکهای سخت (Hard-Link)
برخی توسعهدهندگان برای صرفهجویی در زمان و فضای دیسک در دایرکتوریهای حجیم vendor/ در PHP، از cp -al (کپی لینک سخت) استفاده میکنند. با این حال، در اکثر توزیعهای لینوکس، تنظیم fs.protected_hardlinks=1 به صورت پیشفرض فعال است. این تنظیم کرنل از ایجاد لینک سخت برای فایلی که کاربر مالک آن نیست، جلوگیری میکند.
نکته حیاتی این است که cp -al وقتی این اتفاق میافتد، خطای شدیدی نمیدهد. در یک مورد، ۱۸۰۰ فایل متعلق به root بیصدا نادیده گرفته شدند. worktree کامل به نظر میرسید، اما اپلیکیشن خراب بود.
برای رفع این مشکل، یک رویکرد دو مرحلهای لازم است:
- اول، اجرای
cp -al "$MAIN/backend/vendor" "$WT/backend/vendor". - دوم، اجرای
cp -a --no-clobber "$MAIN/backend/vendor/." "$WT/backend/vendor/"برای پر کردن شکافهای باقیمانده.
آلودگی محیطی داکر
بسیاری از پروژهها چکاوت اصلی را به صورت bind-mount به کانتینرهای داکر متصل میکنند. وقتی یک عامل دستور docker compose exec app php artisan test را از داخل یک worktree اجرا میکند، در واقع در حال تست کردن کدهای چکاوت اصلی است، نه تغییراتی که همین حالا نوشته است.
هیچ راه حل بومی در گیت برای این مشکل وجود ندارد. راه حل این است که از یک مرحله Setup استفاده شود که یک فایل .env کپی شده ایجاد کند. این فایل باید تسترنر را به پورتهای Host-exposed کانتینرها (برای دیتابیس و کش) متصل کند. بدین ترتیب تستها روی Host و داخل worktree اجرا میشوند و داکر فقط سرویسهای پشتیبان را فراهم میکند.
نشت کامیتهای Squash
اسکریپتهای Setup که فایلهای ردیابیشده (مانند backend/.env.testing) را برای تغییر پورتها بازنویسی میکنند، ریسک جدیدی ایجاد میکنند. از آنجایی که عاملها اغلب برای ایجاد نقاط بازگشت (checkpoints) از git add -A استفاده میکنند، این تغییرات محیطی محلی به هر کامیت نشت میکنند.
اگر این مورد مدیریت نشود، کامیت نهایی (Squash commit) این تغییرات را به شاخه میبرد و تنظیمات داکر را برای تمام توسعهدهندگان دیگر تیم خراب میکند.
سیاستهای سختگیرانه (Enforcement Policy)
برای جلوگیری از این اتفاق، سختگیری باید در زمان Squash رخ دهد، نه در زمان Setup. این فرآیند مراحل زیر را دنبال میکند:
۱. اجرای git add -A
۲. اجرای git reset --soft "$BASE_SHA"
۳. شناسایی مسیرهای Stage شده که با یک glob محافظتشده مطابقت دارند از طریق git diff --cached --name-only -z.
۴. اجرای git reset -q "$BASE_SHA" -- "${excluded[@]}" برای بازگرداندن فایلهای محافظتشده به حالت پایه.
هر فایل محافظتشدهای که توسط عامل ایجاد شده و در حالت پایه وجود نداشته است، کاملاً از index حذف میشود. سپس VADD رویدادی را صادر میکند که نام تمام مسیرهای حذف شده را ذکر میکند تا در مورد آنچه کامیت شده است، به کاربر «دروغ» نگوید.
تداخلهای موازی
ایزولاسیون زمانی شکست میخورد که اسکیمای دیتابیسها منحصربهفرد نباشند. در VADD، یک جدول plan-task در ابتدا از شماره ترتیبی تسک ('0', '1', '2') به عنوان کلید اصلی جهانی استفاده میکرد. در حالی که این شماره در یک هدف (Objective) منحصربهفرد است، اما در چندین هدف موازی نیست.
وقتی هدف دوم به مرحله برنامهریزی رسید، با خطای UNIQUE مواجه شد. چون این خطا بلعیده شده بود، کاربر یک پلان بدون ردیف میدید. این باگ پنهان ماند زیرا تستها هر بار فقط یک هدف را اجرا میکردند. درس این است: اگر میخواهید کارهای موازی را ایزوله کنید، تستهای شما هم باید واقعاً به صورت موازی اجرا شوند. برای بهینهسازی این لایهها، برخی ابزارها از زبانهای سیستمی استفاده میکنند؛ برای مثال استفاده از Rust در ابزارهای گیت میتواند مدیریت موازی عاملها را به شکل بسیار سادهتری پیادهسازی کند.
این تغییر در رویکرد به این معناست که توسعهدهندگان دیگر نمیتوانند به وضعیت داخلی گیت به عنوان منبع حقیقت برای سلامت سیستم فایل اعتماد کنند. بار تایید صحت از ابزار به لایه اتوماسیون (wrapper) منتقل میشود.
برای کسانی که گردش کارهای عاملمحور (agentic workflows) میسازند، درس روشن است: فرض کنید سیستم فایل به شما دروغ میگوید. هر حذف باید روی دیسک تایید شود و هر Setup باید در صورت شکست، به عنوان یک توقف سخت (hard stop) در نظر گرفته شود.
برای اجتناب از این تلهها، با بازبینی bind-mountها و ساختارهای دسترسی (permissions) پروژه خود شروع کنید. پیادهسازی کامل این اصلاحات در سورس کد سرور VADD در مسیر packages/server/src/git/ و لیست کامل تلهها در فایل CLAUDE.md موجود است.
گام بعدی شما
- ساختار bind-mountها و دسترسیهای فایل (Permissions) پروژه خود را بازبینی کنید.
- برای هر محیط ایزوله، یک مرحله Setup اجباری تعریف کنید که پیش از اجرای هر تسک، صحت وابستگیها را تایید کند.
- در اتوماسیونهای گیت، هرگز به خروجی دستورات اکتفا نکنید و وضعیت فیزیکی دیسک را با
existsSyncچک کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو