موازیسازی با گفتن «این را بین چند agent تقسیم کن» شروع نمیشود. نخست باید نشان دهیم خروجیها مستقلاند، مرز فایل و رفتار روشن است و integration owner میداند چه چیزی را با چه قراردادی مصرف میکند. این درس overlap بد را آشکار و یک نقشهٔ مالکیت برای `booking-platform` میسازد.
## اهداف یادگیری
پس از درس میتوانی dependency و استقلال را آزمون کنی؛ file ownership، consumed/produced interface و conflict rule بنویسی؛ کار بدِ همپوشان را رد کنی؛ integration owner تعیین کنی؛ و اختلاف contract، schema و evidence را به مسیر تصمیم برگردانی.
## جایگاه در چرخه
این درس بین Slice و Implement قرار دارد. Task Graph فصل ۴ میگوید چه چیزی لازم است؛ اینجا مشخص میکنیم کدام گرهها موازی و کدام ترتیبیاند. قاعده: اگر worker برای شروع به تغییر منتشرنشدهٔ worker دیگر نیاز دارد، مستقل نیست؛ اگر دو worker یک فایل، migration، interface یا runtime state را مینویسند، بدون قرارداد صریح overlap دارند.
## سه قرارداد لازم
**مالکیت فایل** یعنی در هر زمان یک owner نویسندهٔ نهایی یک path یا symbol است. read-only مصرفکننده میتواند چندتا باشد. مالکیت directory کافی نیست: دو worker ممکن است هر دو `packages/contracts/src/notification.ts` را تغییر دهند. generated file یا lockfile نیز owner مشخص میخواهد.
**Interface مصرف/تولید** باید نام artifact، schema/version، producer، consumer، acceptance test و زمان آمادهبودن داشته باشد. مثال:
producer: contracts-owner
produces: NotificationMessage v1 { tenantId, appointmentId, channel, body }
consumer: email-adapter, sms-adapter
compatibility: additive fields; unknown fields ignored
evidence: contract test CT-NOTIFY-01..03**Conflict rule** میگوید هنگام اختلاف چه میشود: یک owner تصمیم میگیرد، version additive است، migration فقط integration owner اجرا میکند، و هیچ workerی conflict را با overwrite حل نمیکند. تغییر acceptance، امنیت یا policy همیشه `DECIDE` است، نه merge خودکار.
فقط کار مستقل موازی
سه شرط همزمان لازم است: dependency ندارند یا dependency immutable و منتشرشده است؛ خروجیهای نوشتاری جدا دارند؛ و هر worker میتواند با fixture/runtime جدا verification کند. تستهای shared و تغییر schema مرکزی معمولاً integration task هستند. «هر task کوچک است» دلیل استقلال نیست.
## مثال بد و خوب
بد: worker A UI فرم reschedule، worker B همهٔ API، worker C migration و worker D تست E2E. همه بهطور همزمان `appointment.ts`، `package.json` و fixture مشترک را تغییر میدهند. B contract را عوض میکند، A body قدیمی میفرستد، C ستون را rename میکند و D چون branch خودش کامل نیست تست را skip میکند.
خوب: ابتدا owner قرارداد `NotificationMessage v1` را میسازد و contract test را منتشر میکند. سپس email، SMS و in-app adapter هرکدام در worktree و فایل های جدا کار میکنند. integration owner فقط merge و wiring مشترک، matrix CI و full verification را انجام میدهد. اگر adapter به تغییر contract نیاز داشت، proposal میدهد؛ خودش contract را silently تغییر نمیدهد.
## سناریوی booking-platform
برای reschedule، این نقشه قابل قبول است:
contracts: packages/contracts/src/notification.ts (owner: contract)
email: apps/api/src/notifications/email-adapter.ts (owner: email)
sms: apps/api/src/notifications/sms-adapter.ts (owner: sms)
in-app: apps/web/src/notifications/in-app-adapter.ts (owner: in-app)
wiring + DI: apps/api/src/notifications/index.ts (owner: integration)
fixtures: packages/test-fixtures/notifications/* (owner: integration)هر adapter `NotificationPort` را مصرف و fake provider خودش را تولید میکند. فایلهای shared فقط با review integration تغییر میکنند. adapterها حق تغییر policy زمانبندی، tenant isolation یا reschedule transaction را ندارند. این مرز باعث میشود سه کار مستقل باشند، نه اینکه domain را سه بار بازنویسی کنند.
## آزمایشگاه
**ورودی:** Task Graph، قرارداد پیام v1، چهار مسیر مالکیت بالا و Spec reschedule. ابتدا جدولی با ستونهای `task, owner, reads, writes, consumes, produces, dependency, conflict rule, evidence` بنویس. سپس برای هر adapter یک worktree و fake provider ایجاد کن.
نمونهٔ فرمان امن در repository فرضی:
git worktree add -b agent/notify-email ../wt-email origin/main
pnpm exec vitest run packages/contracts/src/notification.contract.test.ts**خروجی مورد انتظار:** سه worker بدون write مشترک، یک contract test سبز، interface versionدار و یک integration task صریح. **Recovery:** اگر دو owner یک path را رزرو کردند، یکی را read-only کن یا task را به integration منتقل کن. اگر contract test شکست خورد، merge را متوقف و proposal version جدید بساز؛ fixture را برای سبز شدن مصنوعی تغییر نده. اگر dependency منتشر نشده است، worker را `BLOCKED` نگه دار.
## خطاهای رایج
- استقلال را از اندازهٔ ticket نتیجه گرفتن.
- مالکیت را فقط بر اساس پوشه نوشتن.
- contract را در prompt نگه داشتن و در فایل/تست ثبت نکردن.
- shared lockfile یا generated client را بیصاحب رها کردن.
- merge کردن branch سبز بدون اجرای consumer contract.
- گذاشتن integration owner بهعنوان «هرکس فرصت داشت».
## Verification
- [ ] هر task producer، consumer و dependency صریح دارد.
- [ ] هیچ دو workerی writer یک فایل یا symbol نیستند.
- [ ] interface schema/version و contract test دارد.
- [ ] conflict rule برای contract، schema، test و policy ثبت شده است.
- [ ] integration owner و تصمیمگیرندهٔ `DECIDE` معلوماند.
- [ ] bad overlap با مثال واقعی رد شده است.
- [ ] branch-level green جای full integration evidence را نگرفته است.
## تمرین و Rubric
یک epic «یادآوری رزرو» را به چهار task تقسیم کن. پاسخ خوب فقط adapterها را موازی میکند؛ contract و wiring owner جدا دارد؛ lockfile/generated output را مالکدار میکند؛ consumed/produced interface، conflict rule و contract test میدهد؛ و migration یا تغییر shared policy را بدون integration owner موازی نمیکند.
## آزمون استقلال پیش از واگذاری
پیش از ساخت worktree، برای هر task پنج پرسش بپرسید. آیا ورودیهای آن در یک commit یا artifact immutable موجود است؟ آیا خروجیاش بدون تغییر فایل دیگری قابل ارزیابی است؟ آیا دو task روی یک invariant، migration یا قرارداد write نمیکنند؟ آیا failure آن میتواند بدون rollback کار دیگری recovery شود؟ و آیا یک owner میتواند نتیجه را در integration مصرف کند؟ پاسخ منفی به هرکدام نشانهٔ dependency پنهان است، نه دلیلی برای افزودن هماهنگکننده.
این آزمون بهخصوص برای فایلهای ظاهراً بیاهمیت مهم است. `package.json`، lockfile، generated client، snapshot و fixture shared هم interface هستند. تغییر یک dependency میتواند رفتار هر سه adapter را تغییر دهد؛ پس owner نسخه و زمان ادغام باید مشخص باشد. اگر worker فقط یک test جدید مینویسد اما test به database یا clock مشترک وصل است، خروجی آن مستقل نیست. fake clock، fixture محلی و port اختصاصی میتواند dependency اجرایی را به قرارداد قابل کنترل تبدیل کند.
Ownership را هم بهصورت رزرو و هم بهصورت review بررسی کنید. رزرو اولیه پیش از شروع جلوی کار تکراری را میگیرد؛ review نهایی بررسی میکند که diff واقعاً در مرز رزروشده مانده است. اگر worker بهاجبار path مشترکی را تغییر داد، آن بخش را به integration owner منتقل کنید یا قرارداد را versionدار کنید. merge conflict صرفاً علامت syntax نیست؛ معمولاً نشانهٔ آن است که decomposition یا ownership پیش از fan-out کافی نبوده است.
برای هر خروجی یک handoff کوتاه بنویسید: commit پایه، فایلهای خواندهشده، فایلهای نوشتهشده، interface مصرفشده، interface تولیدشده، commandهای اجراشده، نتیجه و limitation. این handoff باعث میشود worker بعدی context را از نو حدس نزند و در عین حال ادعای «همهچیز آماده است» بدون evidence پذیرفته نشود.
## جمعبندی
موازیسازی زمانی ایمن است که taskها استقلال رفتاری و نوشتاری داشته باشند. مالکیت فایل، قرارداد interface و قانون conflict، زبان مشترک worker و integration owner هستند. هر overlap ناشناخته هزینهٔ هماهنگی و احتمال merge شکستخورده را بالا میبرد.
## منابع مستقیم
- [Anthropic — Building effective agents](https://www.anthropic.com/engineering/building-effective-agents) — الگوهای delegation و trade-off؛ نسخهٔ سازمانی خاصی تحمیل نمیکند.
- [Git worktree documentation](https://git-scm.com/docs/git-worktree.html) — پایهٔ ایزولیشن branch؛ ownership محصولی نیست.
- [Martin Fowler — Continuous Integration](https://martinfowler.com/articles/continuousIntegration.html) — feedback زودهنگام؛ جای contract design را نمیگیرد.
- [Cucumber contract/testing guidance](https://cucumber.io/docs/guides/10-minute-tutorial/) — **سطح شواهد: راهنمای رسمی ابزار؛** مثال executable specification است، اما قالب contract test این دوره را اثبات نمیکند؛ رکورد آن در `sources.md` ثبت شده است.
به زبان ساده
موازیسازی زمانی امن است که taskها writer مشترک نداشته باشند، interface versionدار داشته باشند و هرکدام بتوانند مستقل verify شوند. فایل مشترک، migration یا acceptance معمولاً باید مالک integration داشته باشد.
مثال واقعی
در نقشهٔ فرضی دوره، email و SMS adapterها contract پیام ثابت را مصرف میکنند، اما تغییر `packages/contracts` و wiring فقط به owner ادغام سپرده میشود.