بازگشت به کتابخانهکتابخانه9.2یک Story در هر Context، صف وابستگی و State ماندگار
مهندسی نرم‌افزار ایجنت‌محور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 9.2فصل ۹ — Ralph و اجرای طولانی‌مدت

یک Story در هر Context، صف وابستگی و State ماندگار

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

اجرای طولانی وقتی قابل‌اعتماد می‌شود که هر session بداند دقیقاً کدام story را می‌سازد، چه چیزی را نباید تغییر دهد و session بعدی از کجا ادامه دهد. `tasks.json` صف تصمیم‌گیری نیست؛ نقشه‌ای dependency-ranked است. `progress.md` هم دفتر خاطرات مدل نیست؛ گزارش evidenceدار است که orientation و handoff تازه را ممکن می‌کند.

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

پس از درس می‌توانی یک story را به context محدود و self-contained تبدیل کنی؛ taskهای آماده را بر اساس dependency و risk رتبه‌بندی کنی؛ acceptance را immutable نگه داری؛ progress خوب را از progress گمراه‌کننده تشخیص دهی؛ Git history، orientation و fresh-session handoff بسازی؛ و recovery را از روی commit و state انجام دهی.

## جایگاه در چرخهٔ سرتاسری

درس قبل pattern را معرفی کرد. اکنون `Slice → Test → Implement` را به واحدهای قابل‌تحویل تبدیل می‌کنیم. ورودی این درس Spec، acceptance examples، تست محافظت‌شده و permission profile فصل‌های قبل است. خروجی آن backlog اجرایی و state durable برای درس ۳۴ است.

## one-story-per-context

Context باید یک outcome قابل‌بررسی داشته باشد، نه یک epic. برای booking-platform این story مناسب است: «برای دو درخواست مجاز هم‌زمان روی یک slot، یکی `200` و دیگری `409 SLOT_CONFLICT` بده و هیچ audit اضافی تولید نکن.» reminder، UI، migration retirement و calendar sync storyهای دیگری‌اند.

مثال بد

{"id":"reschedule-all","task":"کل reschedule، reminder، calendar و migration را بساز",
 "acceptance":["تست‌ها سبز شوند"]}

این item dependency پنهان، acceptance مبهم و چند مالک دارد. مدل می‌تواند یکی را انجام دهد و سه تای دیگر را وانمود کند.

مثال خوب: `tasks.json`

{
  "tasks": [
    {"id":"S2","dependsOn":["S1"],"risk":1,"status":"ready",
     "story":"race روی slot واحد را ثابت کن",
     "acceptance":["یک 200 و یک 409 SLOT_CONFLICT","در success دقیقاً یک mutation و یک audit","در failure صفر mutation و صفر audit تازه"],
     "verify":["vitest --run reschedule.race.test.ts","independent-review"]},
    {"id":"S3","dependsOn":["S2"],"risk":2,"status":"blocked",
     "story":"replay idempotency را اضافه کن",
     "acceptance":["body/status durable","audit یکتا"],
     "verify":["replay integration test"]}
  ]
}

`acceptance` فقط با تصمیم صاحب محصول/Spec تغییر می‌کند. Agent حق ندارد برای سبز شدن تست آن را حذف، ضعیف یا به متن تبدیل کند. `risk` و `dependsOn` ترتیب را توضیح می‌دهند؛ عدد کمتر به معنی اولویت بالاتر در این قرارداد است. اگر دو task ready و هم‌ریسک‌اند، انسان tie-break را ثبت می‌کند.

چرا State باید چندلایه باشد؟

هیچ artifact واحدی همهٔ پرسش‌های handoff را پاسخ نمی‌دهد. `tasks.json` می‌گوید چه چیزی آماده است و چرا؛ acceptance می‌گوید pass دقیقاً چه معنایی دارد؛ `progress.md` می‌گوید در آخرین session چه دیده شده؛ و Git نشان می‌دهد واقعاً چه چیزی تغییر کرده است. اگر فقط progress را نگه داریم، مدل می‌تواند ادعای سبز بودن را بدون diff ثبت کند. اگر فقط Git را نگه داریم، معلوم نیست چرا یک task انتخاب شده یا کدام acceptance هنوز باز است. اگر فقط tasks را نگه داریم، crash وسط تست و budget باقی‌مانده گم می‌شود.

برای هر transition یک رابطهٔ قابل‌بررسی بنویس: `ready → in_progress` فقط وقتی dependencyها pass یا صریحاً waived شده‌اند؛ `in_progress → verified` فقط پس از اجرای command ثبت‌شده؛ و `verified → complete` فقط پس از commit و تأیید مستقل. این stateها را با متن آزاد جایگزین نکن. یک timestamp به‌تنهایی evidence نیست؛ باید command، محیط، revision و خروجی نیز معلوم باشد. در booking-platform، خروجی concurrency باید دقیقاً یک `200` و یک `409 SLOT_CONFLICT`، یک audit در success و zero mutation/audit در failure را ثبت کند. عبارت «race حل شد» بدون شمارش و fixture قابل‌قبول نیست.

همچنین state durable نباید بهانه‌ای برای نگه‌داشتن log نامحدود باشد. log خام را در artifact جدا و خلاصهٔ قابل‌خواندن را در progress نگه دار؛ secret، token و دادهٔ tenant را redact کن؛ و هر summary را به commit یا فایل مشخص link بده. به این ترتیب session تازه هم signal کافی دارد و هم با context rot ناشی از تاریخچهٔ بی‌پایان روبه‌رو نمی‌شود.

## progress خوب و گمراه‌کننده

**خوب:**

2026-09-05T18:20Z | S2 | 2/3
Done: fixture barrier و test A-08
Evidence: commit abc123؛ 100 runs: 100 one-200/one-409
Open: audit count در retry هنوز verify نشده
Next: فقط audit assertion؛ command و path مشخص
Decision: S3 blocked تا S2 verifier امضا کند

**گمراه‌کننده:**

S2 تقریباً تمام شد. کد race درست است و تست‌ها سبز هستند.

دومی زمان، commit، شمارش اجرا، open risk و next action ندارد؛ «تقریباً» قابل orientation نیست و ممکن است سبز شدن از mock یا اجرای تست اشتباه آمده باشد.

## Git history و handoff

هر story باید commit کوچک و قابل‌تفسیر داشته باشد؛ message باید outcome را بگوید، نه «changes». قبل از handoff این موارد را ثبت کن: branch/commit، فایل‌های تغییرکرده، تست و خروجی، acceptanceهای pass/fail، تصمیم‌های باز، task بعدی، محیط و recovery. Session تازه ابتدا orientation می‌کند: `git status`، آخرین commitها، `tasks.json`، progress، Spec و commandهای verification را می‌خواند؛ سپس تنها task آماده را انتخاب می‌کند. حضور log قدیمی بدون خلاصهٔ current state کافی نیست.

## سناریوی booking-platform

پس از S1، taskهای S2 concurrency، S3 idempotency و S4 privacy در صف‌اند. S3 نباید قبل از transaction boundary S2 اجرا شود. S4 می‌تواند بخشی مستقل از S3 باشد، اما وقتی به error serialization S1 وابسته است باید dependency آن صریح باشد. هر context تنها یکی از این storyها را می‌گیرد؛ taskهای «همهٔ errorها» یا «DB را تمیز کن» حذف یا بازنویسی می‌شوند.

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

**ورودی:** Spec reschedule، acceptance A-08، سه task نمونه و repository آزمایشگاهی بدون secret.

۱. `tasks.json` را با `id`, `dependsOn`, `risk`, `status`, `acceptance`, `verify` کامل کن. ۲. یک commit baseline بساز. ۳. orientation note بنویس. ۴. فقط S2 را اجرا و خروجی تست را به progress اضافه کن. ۵. crash مصنوعی session را با بستن process شبیه‌سازی کن. ۶. session تازه از Git و progress ادامه دهد.

فرمان قابل‌تکرار آزمایشگاه، روی یک پوشهٔ موقت و بدون network است:

git status --short
node ./lab-orient.mjs --tasks ./tasks.json --progress ./progress.md
npm test -- --runInBand
git log -1 --format='%H %s'

`lab-orient.mjs` در این مثال stub آموزشی است؛ اگر وجود نداشت، آن را از package ناشناخته دانلود نکن. دانشجو باید خروجی command، زمان و commit را کنار progress ذخیره کند. برای شبیه‌سازی crash، process آزمایشی را فقط در workspace disposable متوقف کن و هرگز migration یا command دارای side effect را تکرار نکن. session دوم ابتدا `git diff` و `state.json` را می‌خواند، سپس فقط verification read-only را اجرا می‌کند. اگر task به‌اشتباه ready علامت خورده باشد، آن را به `BLOCKED` برگردان و owner dependency را مشخص کن. این recovery عمداً کندتر از ادامهٔ کور است، چون duplicate mutation از دست دادن چند دقیقه پرهزینه‌تر است.

**خروجی مورد انتظار:** session دوم بدون تکیه به حافظهٔ session اول task، commit، تست و open risk را پیدا می‌کند. **Recovery:** اگر progress با Git تناقض داشت، Git و artifact تست را authority فنی بدان، task را `BLOCKED` کن و اختلاف را با owner ثبت کن؛ progress را برای پوشاندن diff بازنویسی نکن.

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

  • انتخاب task بر اساس متن صف، نه dependency و risk.
  • تغییر acceptance در همان commit implementation.
  • progress بدون commit، command یا خروجی خام.
  • handoff با «ادامه بده» و بدون مسیر فایل.
  • علامت‌زدن task بعدی به‌عنوان آماده در حالی که verifier قبلی اجرا نشده است.

## Verification

  • [ ] هر context دقیقاً یک story و یک outcome دارد.
  • [ ] صف dependency-ranked است و چرخهٔ dependency ندارد.
  • [ ] acceptance در فایل جدا/بخش محافظت‌شده و immutable است.
  • [ ] progress شامل timestamp، commit، command، evidence، open risk و next است.
  • [ ] orientation و fresh-session handoff تکرارپذیر است.
  • [ ] Git history و recovery با state متناقض برخورد fail-safe دارند.

## تمرین و Rubric

دو task «بهبود query» و «ثبت audit یکتا» را برای S3 رتبه‌بندی کن و یک progress خوب بنویس.

**Rubric:** dependency واقعی و risk protected write اولویت audit را توضیح دهد؛ acceptance قابل‌اندازه‌گیری و تغییرناپذیر باشد؛ progress commit، command، output، open risk و next action داشته باشد؛ و برای crash یا تناقض state مسیر `BLOCKED` و owner مشخص کند.

## جمع‌بندی

Long-running execution با حافظهٔ مدل پایدار نمی‌شود؛ با story کوچک، acceptance ثابت، صف وابسته، Git و progress evidenceدار پایدار می‌شود. Fresh session باید بتواند بدون حدس orientation کند و اگر state معتبر نیست، متوقف شود.

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

  • [Anthropic — Effective harnesses for long-running agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents) — گزارش میدانی دربارهٔ تقسیم کار، progress و handoff.
  • [Git documentation — git-log](https://git-scm.com/docs/git-log) — مستند رسمی برای خواندن history؛ وجود commit به‌تنهایی صحت رفتار را ثابت نمی‌کند.
  • [Geoffrey Huntley — Ralph](https://ghuntley.com/ralph/) — منبع جامعه‌محور pattern؛ acceptance و safety این درس طراحی آموزشی تکمیلی‌اند.

به زبان ساده

هر Context طولانی باید به یک story با Outcome و acceptance ثابت محدود شود. `tasks.json` صف dependency و risk است و `progress.md` باید commit، فرمان، نتیجه، ریسک باز و گام بعد را ثبت کند.

مثال واقعی

در صف فرضی دوره، S2 «یک ۲۰۰ و یک ۴۰۹ برای دو درخواست هم‌زمان» پیش‌نیاز S3 replay است؛ session بعدی از commit و progress ادامه می‌دهد، نه از حافظهٔ مدل قبلی.

دانش‌سنجی

آزمون درس

۵ Q
01
چرا one-story-per-context مهم است؟
02
tasks.json باید چه چیزی را رتبه‌بندی کند؟
03
کدام progress evidenceدار است؟
04
اگر acceptance برای سبزشدن تست تغییر کند چه می‌کنی؟
05
orientation session تازه از کجا شروع می‌شود؟

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

tasks مربوط به S2، S3 و S4 را رتبه‌بندی کن و progress گمراه‌کننده را اصلاح کن.