بازگشت به کتابخانهکتابخانه12.2TDD، اجرای Agent، Ralph محدود و Worktreeهای مستقل
مهندسی نرم‌افزار ایجنت‌محورAGENTIC SOFTWARE ENGINEERING — از Grilling و Context Engineering تا TDD، Ralph، Subagent، امنیت و تحویل قابل‌اعتماد
v1.0.0
01واقعیت مهندسی ایجنت‌محور
02Grilling و زبان مشترک
03Context Engineering
04Spec-Driven Development و برنامهٔ اجرایی
05Vertical Slices و تحویل تدریجی
06TDD با ایجنت‌ها
07گردش‌کار روزانه، Debug و UI Prototyping
08ساخت هارنس — Rules، Skills، Hooks و MCP
09Ralph و اجرای طولانی‌مدت
10Subagent، Worktree و ارکستراسیون موازی
11Verification، Security و مقیاس تیمی
12پروژهٔ نهایی سناریومحور
LESSON 12.2فصل ۱۲ — پروژهٔ نهایی سناریومحور

TDD، اجرای Agent، Ralph محدود و Worktreeهای مستقل

  • ~۸۲ دقیقه
  • ۵ پرسش
  • متن را انتخاب کن تا هایلایت شود

Artifact خوب هنوز feature نیست. در این درس همان ExecPlan را به اجرای قابل‌ممیزی تبدیل می‌کنی: انسان acceptance و تست را تأیید می‌کند، Red واقعی دیده می‌شود، هر execution فقط یک Slice دارد، Oracle محافظت می‌شود و worktree از runtime هم جدا می‌گردد. Ralph فقط وقتی مجاز است که bounded و قابل‌بازیابی باشد.

## اهداف یادگیری

می‌توانی تست approved را از implementation جدا کنی؛ Red مشاهده‌شده و baseline را ثبت کنی؛ یک Slice را در یک context اجرا کنی؛ protected oracle را از reward-hacking حفظ کنی؛ namespace برای branch، port، database، cache، process و credential جعلی بسازی؛ fan-out محدود را توجیه کنی؛ و Ralph را با budget و evidence recovery اجرا یا رد کنی.

## جایگاه در چرخه

این درس `Test → Implement → Observe` است. ورودی S0 تا S6 و تست‌های acceptance درس ۴۳ است؛ خروجی commitهای کوچک، Red/Green evidence، isolation manifest، progress log و نتیجهٔ `COMPLETE|BLOCKED|DECIDE` است.

## تست انسانی و Red

انسان باید behavior، assertion، fixture، scope، ممنوعیت و فرمان را تأیید کند. برای S2 یک test concurrency باید invariant را بسنجد: برای slot یکسان دقیقاً یک `200` و یک `409 SLOT_CONFLICT`، فقط یک appointment interval و فقط یک audit؛ failure نباید mutation یا audit داشته باشد. ابتدا روی baseline اجرا می‌شود؛ اگر به‌علت نبود behavior شکست رفتاری دارد، log و diff test ثبت می‌شود. شکست import، dependency یا environment، Red معتبر نیست.

Approved by: product-owner + test-owner
Slice: S2 only
Allowed: appointments/reschedule, protected-write fixture, its tests
Forbidden: changing acceptance, weakening assertion, migration خارج S2,
           network, real notification, production/staging credential
Done: observed Red → minimal Green → fresh verifier → evidence

Protected oracle در فایل approval و verifier مستقل نگهداری می‌شود. Agent نویسنده نمی‌تواند expected را برای سبزشدن عوض کند، تست را حذف کند، concurrency را با sleep جا بزند یا assertion روی mutation/audit را کم کند. اگر oracle غلط است، خروجی `DECIDE` و تغییر آن با owner انسانی است.

## مثال بد و اصلاح‌شده

بد: سه agent در سه branch، همه روی `localhost:5432` و cache یکسان، سپس یک Ralph نامحدود با `--approve-all` و staging token اجرا می‌کنند. branch جداست، اما واقعیت runtime مشترک و recovery نامعلوم است.

خوب: `run-20260905-s2-01` در worktree اختصاصی، DB `booking_s2_01`، API port 4312، cache prefix `s2-01:`، fake provider، network deny، owner و TTL دارد. Verifier تازه در process جدا fixture جدید می‌سازد. هر execution فقط S2 است.

## یک fan-out، نه موازی‌سازی نمایشی

fan-out فقط وقتی مجاز است که ownership و state مستقل باشد. در این پروژه می‌توان S2 concurrency و S4 privacy را موازی کرد، چون فایل‌های اصلی و acceptance متفاوت، dependency مشترکِ فقط S1 و قرارداد integration روشن دارند. S3 idempotency تا مرز transaction S2 روشن نشده، موازی نمی‌شود. fan-out پیشنهادی:

Base S1
 ├─ worker-s2: barrier/race + protected-write fixture
 └─ worker-s4: authorization/error serialization + privacy log guard
Integration contract: no shared runtime; status codes frozen; S4 cannot
change S2 oracle; owner=integration-reviewer; merge S2 then S4

اگر ownership overlap، migration مشترک یا runtime shared رخ داد، fan-out لغو و کار به context ترتیبی برمی‌گردد. تعداد agent معیار بهره‌وری نیست.

## Ralph محدود و recovery

Ralph اینجا optional است و فقط برای یک task باریک S2 در sandbox قابل‌قبول است: حداکثر ۸ iteration، ۲۰ دقیقه wall-clock، spend نمادین `EXAMPLE_BUDGET_DO_NOT_CHARGE`، network deny، command allowlist، acceptance immutable و verifier مستقل. حالت‌ها `ORIENT → SELECT → IMPLEMENT → VERIFY → COMMIT` هستند؛ پایان فقط `COMPLETE`، `BLOCKED` یا `DECIDE`.

پیش از هر iteration، `state.json` با runId، taskId، iteration، phase، lastCommit و budget باقی‌مانده atomic نوشته می‌شود. patch و خروجی test artifact می‌شوند و commit بعد از verification است. crash یعنی session تازه status، state و diff را بخواند و mutation را کورکورانه تکرار نکند. timeout با kill کردن child process و flush، `BLOCKED` است؛ policy ambiguity، `DECIDE`.

## آزمایشگاه گام‌به‌گام

**ورودی:** approved S2 test، ExecPlan، baseline و worktree root. در sandbox:

BASE_REF="$(git symbolic-ref --quiet --short HEAD || true)"
if [ -z "$BASE_REF" ]; then
  BASE_REF="$(git branch --show-current)"
fi
test -n "$BASE_REF" || { echo "BLOCKED: detached HEAD; choose an approved local commit"; exit 1; }
git show-ref --verify --quiet "refs/heads/$BASE_REF" ||
  { echo "BLOCKED: local base branch unavailable; use an approved commit"; exit 1; }
git worktree add -b agent/s2-run ../wt-s2 "$BASE_REF"
git -C ../wt-s2 diff --check
git -C ../wt-s2 status --short
git worktree list --porcelain

این فرمان به snapshot محلی و branch جاری تکیه دارد، نه `origin/main`. اگر HEAD detached باشد یا branch محلی موجود نباشد، اجرای ساخت متوقف می‌شود؛ recovery این است که integration owner یک commit پایهٔ مشخص و locally موجود را با checksum در manifest تأیید کند و سپس `git worktree add -b agent/s2-run ../wt-s2 <approved-commit>` را اجرا کند. fetch یا ساختن remote در میانهٔ آزمایش مجاز نیست، چون مبنای مشاهده را عوض می‌کند. اگر نام branch قبلاً استفاده شده، مسیر و branch را با run-id تازه و تأیید owner انتخاب کن، نه `--force`.

**مراحل و خروجی:** (۱) isolation manifest با branch/path/port/DB/cache/process/ generated/fake credential/TTL بنویس؛ (۲) baseline را ثبت کن؛ (۳) تست approved را بدون implementation اجرا و Red رفتاری را ذخیره کن؛ (۴) Agent را با همان یک Slice و allowlist اجرا کن؛ (۵) Green، diff و test output را ثبت کن؛ (۶) verifier تازه را در DB تازه اجرا کن؛ (۷) در صورت نیاز crash iteration دوم را شبیه‌سازی و recovery log بنویس؛ (۸) فقط پس از pass commit کن.

**Recovery:** اگر worktree branch موجود است rename یا owner را بررسی کن، force نکن. اگر baseline fail است آن را به Red نسبت نده. اگر test تغییر کرد، diff تست را reject و approval را دوباره بگیر. اگر port/DB مشترک بود execution را متوقف، resource را isolate و evidence قبلی را quarantine کن. اگر budget تمام شد، آخرین state را flush و `BLOCKED` بده.

## خطاهای رایج و نشانه‌های تشخیص

  • **«تست سبز» بدون Red:** log اولین اجرای رفتاری وجود ندارد یا اولین failure

import/fixture است، نه assertion مربوط به behavior.

  • **چند Slice در یک execution:** task id چند outcome دارد و diff هم‌زمان

S2/S3 یا refactor عمومی را نشان می‌دهد؛ علت failure دیگر قابل‌تشخیص نیست.

  • **worktree جعلی:** branch جداست اما port/DB/cache نام run-id ندارد یا دو

process به یک PID file اشاره می‌کنند.

  • **verifier هم‌فرض:** همان session و همان fixture implementation را داوری

می‌کند؛ نشانه، نبود process تازه و evidence مستقل است.

  • **Ralph بی‌سقف:** retry داخلی از سقف iteration عبور می‌کند یا timeout child

را نمی‌کشد؛ log باید iteration، budget و process tree را نشان دهد.

  • **oracle دست‌کاری‌شده:** expected، audit assertion یا concurrency barrier در

همان diff تغییر کرده است؛ نتیجه باید `CHANGES_REQUIRED` باشد.

  • **commit زودهنگام:** commit قبل از verifier یا بدون diff check ساخته شده؛

recovery، commit را پاک نمی‌کند و ابتدا owner و evidence را ثبت می‌کند.

## Verification

  • [ ] owner انسانی acceptance و test oracle را تأیید کرده است.
  • [ ] baseline و Red رفتاری قبل از implementation ثبت شده‌اند.
  • [ ] هر execution دقیقاً یک Slice دارد.
  • [ ] oracle شامل mutation، audit و concurrency است و immutable مانده است.
  • [ ] worktree و runtime namespace، TTL و cleanup روشن دارند.
  • [ ] fan-out فقط ownership مستقل و integration contract دارد.
  • [ ] Ralph budget، sandbox، allowlist، verifier و سه خروجی صریح دارد.
  • [ ] crash/timeout/test failure recovery بدون mutation کور ثبت شده است.

## تمرین و Rubric

یک اجرای پیشنهادی را بررسی کن: Red ثبت نشده، دو Slice در یک branch هستند، DB مشترک است، assertion conflict حذف شده و loop ۱۲ بار retry می‌کند. پاسخ باید رد/اصلاح هر مورد، manifest namespace، test approval، recovery و دلیل عدم یا محدودیت fan-out را بدهد. Rubric: test/oracle ۳۰٪، isolation ۲۵٪، bounded execution ۲۰٪، orchestration ۱۵٪، evidence/recovery ۱۰٪؛ حذف assertion شکست مسدودکننده است.

## جمع‌بندی

اجرای Agent زمانی مهندسی است که feedback، اختیار و state قابل‌مشاهده باشند. Red واقعی از Green متمایز است؛ worktree بدون runtime namespace کافی نیست؛ و Ralph بدون budget و recovery فقط retry پرهزینه است. موازی‌سازی باید از graph وابستگی و ownership بیاید، نه از هیجان تعداد worker.

## منابع مستقیم

  • [Martin Fowler — TDD](https://www.martinfowler.com/bliki/TestDrivenDevelopment.html) — چرخهٔ بازخورد؛ oracle سناریو از دامنه می‌آید.
  • [Git worktree documentation](https://git-scm.com/docs/git-worktree.html) — ایزولیشن Git، نه runtime.
  • [Effective harnesses for long-running agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents) — state و handoff؛ تضمین ایمنی نیست.
  • [Codex sandboxing](https://developers.openai.com/codex/sandboxing) — اصل sandbox و permission؛ تنظیمات این پروژه آموزشی است.

به زبان ساده

اجرای capstone فقط پس از تأیید انسانِ acceptance و تست آغاز می‌شود و هر context یک Slice دارد. Red واقعی، namespace runtime، worktree مستقل، Ralph محدود و verifier تازه، زنجیرهٔ اجرای قابل‌ممیزی را می‌سازند.

مثال واقعی

در capstone evidence فرضی، artifact `04-approved-tests-red-green-oracle.md` باید Red پیش از implementation و Oracle حفاظت‌شده را نشان دهد و `05-worktree-runtime-manifest-progress-recovery.md` جداسازی DB، port و credential جعلی را ثبت کند.

دانش‌سنجی

آزمون درس

۵ Q
01
Red معتبر کدام است؟
02
چرا oracle protected است؟
03
worktree چه چیزی را خودکار جدا نمی‌کند؟
04
کدام fan-out توجیه‌پذیرتر است؟
05
Ralph محدود در پایان چه signalهایی دارد؟

سناریوی تشریحی

Agent تست conflict را تغییر داده، روی DB مشترک اجرا می‌شود و loop بعد از crash دوباره mutation را اجرا می‌کند. execution را چگونه متوقف و recover می‌کنی؟