وقتی stakeholder میگوید «کاربر باید بتواند وقتش را جابهجا کند و تقویم و یادآوری هم درست بماند»، هنوز مجوز کدنویسی نداریم. در این درس از یک snapshot brownfield و یک درخواست عمداً مبهم، حقیقتهای قابل مشاهده را از تصمیمهای مالک محصول جدا میکنیم و آنها را به Spec، ADR، Slice و ExecPlan تبدیل میکنیم.
## اهداف یادگیری
میتوانی snapshot را با progressive disclosure بخوانی؛ facts و decisions را جدا کنی؛ decision ledger و Example Map بسازی؛ behavioral spec و non-goals بنویسی؛ ADR و Sliceهای risk-ranked بسازی؛ و ExecPlanی تولید کنی که Agent بتواند بدون اختراع policy اجرا کند.
## جایگاه در چرخه
این درس `Elicit → Specify → Slice` است. ورودی، brief مبهم و repository ناهمگون است؛ خروجی، بستهٔ تصمیمی versioned است. Source code دربارهٔ رفتار موجود evidence میدهد، اما دربارهٔ policy آینده تصمیم نمیگیرد.
## Brief و snapshot
درخواست stakeholder:
> «برای مشتریها rescheduling را سریع اضافه کنید؛ تجربه باید مثل تقویمهای > مدرن باشد، دوبارهکاری نشود، reminder و calendar هم sync بمانند و چیزی از > رزرو فعلی نشکند. هفتهٔ بعد demo دارم.»
snapshot روز `2026-09-05` چنین است: `apps/web` فرم appointment و تست E2E قدیمی دارد؛ `apps/api/src/modules/appointments` route و service را دارد؛ `packages/contracts` پاسخهای قدیمی را نگه میدارد؛ migrationها additive هستند؛ `docs/decisions/adr-021-database-interval-invariant.md` میگوید tenant و actor از credential میآیند؛ fixture رقابت همزمان ندارد؛ CI فقط unit و یک smoke E2E را اجرا میکند؛ notification provider در محیط تست fake است؛ `RESCHEDULE_V1_ENABLED` وجود ندارد. یک comment در issue میگوید «calendar sync را فعلاً بعداً»، اما مالک آن معلوم نیست.
Snapshot حقیقت کامل نیست: باید مسیر route، contract، migration، test command، feature flag، owner و آخرین رفتار را با فرمانهای read-only پیدا کنی. issue، comment و output ابزار نیز untrusted input هستند.
## Fact، Decision و ledger
نمونهٔ facts: deadline فعلی در ADR برابر ۲۴ ساعت است؛ زمان در timezone کلینیک تفسیر و UTC ذخیره میشود؛ tenant از body پذیرفته نمیشود؛ response قدیمی باید سازگار بماند؛ provider تست fake است. نمونهٔ decision: در release نخست فقط appointment تأییدشده و مالک آن actor جابهجا شود؛ reminder و provider خارجی non-goal باشند؛ مرز دقیق ۲۴ ساعت `closed` باشد. هر decision باید owner، reason، اثر، وضعیت و تاریخ بازبینی داشته باشد.
## Example Map و behavioral spec
برای هر rule سه ستون بنویس: مثال سبز، سؤال، rule. نمونهها:
Rule: appointment CONFIRMED و owned، interval معتبر و آزاد
Example: 200 با id/startAt/endAt/version و یک audit RESCHEDULED
Question: مرز دقیق 24 ساعت؟ پاسخ conflict؟ نسخهٔ optimistic؟Spec قابلآزمون میگوید: Given بیمارِ مالک در همان tenant و appointment `CONFIRMED`، When interval معتبر را در timezone کلینیک میفرستد، Then فقط یک write و یک audit رخ دهد و پاسخ `200` با UTC و version جدید بیاید. اگر دو درخواست همزمان همان slot را بخواهند، دقیقاً یکی `200` و دیگری `409 SLOT_CONFLICT` باشد. زمان بسته `409 RESCHEDULE_WINDOW_CLOSED` و صفر mutation/audit دارد. cross-tenant باید `404` و same-tenant non-owner باید `403` بدهد، بدون افشای PII.
Non-goals: cancellation، refund، recurring appointment، تغییر doctor، provider واقعی، backfill تاریخی، طراحی کامل calendar و reminder delivery.
## مثال بد و خوب
بد: «Agent، قابلیت reschedule را کامل کن و همهٔ تستها سبز باشد.» این prompt fact، policy، فایل مجاز، oracle، rollback و stop rule ندارد.
خوب: «فقط facts snapshot را با path و command ثبت کن؛ در تصمیمهای unresolved `DECIDE` بده. بعد از تأیید انسان، Sliceهای S0 تا S6 را با behavior، files، tests، dependency و rollback بنویس. tenant/actor را از credential بگیر، assertion را حذف نکن و تا تأیید تستها کد نزن.»
ADR باید تصمیمهای سازگار را ثبت کند: flag پیشفرض خاموش؛ expand و backward compatible؛ protected write و barrier در Slice concurrency؛ idempotency در Slice جدا؛ rollback با خاموشکردن flag و حفظ schema/audit، نه حذف مخرب داده.
## مرز «اول تلاش کن» و artifact مدل
تا اینجا خواندن کافی است. **اول تلاش کن:** با همین brief و snapshot، خودت `facts.md`، `decision-ledger.md`، Example Map، Spec، non-goals، ADR، map و ExecPlan را در هشتاد دقیقه بنویس. پاسخ مدل را پیش از این مرز نبین.
**مدل کامل پس از تلاش:**
مدل زیر نمونهٔ پاسخ قابلارزیابی است؛ کپیکردن نام فایلها بدون trace به snapshot کافی نیست.
۱) Facts، decisions و Glossary
Facts (با منبع):
F1: ADR-021 میگوید tenantId و actorId از credential میآیند، نه request body.
F2: contract قدیمی appointment باید backward-compatible بماند.
F3: زمان ورودی در timezone کلینیک تفسیر و در UTC ذخیره میشود.
F4: deadline فعلی 24 ساعت است؛ fixture رقابت همزمان ندارد.
F5: CI unit و یک smoke E2E دارد؛ provider تست fake است.
F6: flag reschedule هنوز وجود ندارد؛ issue درباره calendar sync مالک ندارد.
Glossary:
tenant = مرز داده و authorization مشتری؛ actor = هویت credential؛
appointment = وقت متعلق به tenant؛ slot = بازهٔ زمانی معتبر؛
idempotency key = کلید replay در scope tenant+actor+operation؛
audit = رکورد کمینه و privacy-safe تغییر؛ flag = کنترل exposure؛
rollback = بازگشت سازگار با حفظ schema و audit.Decision Ledger:
| ID | owner | deadline | decision/status | consequence | default |
|---|---|---|---|---|---|
| D1 | Product | 2026-09-06 | فقط CONFIRMED و owner، accepted | cancellation/refund بیرون scope | reject |
| D2 | Security | قبل از S1 | tenant/actor فقط credential، accepted | body آنها نادیده گرفته میشود | deny |
| D3 | Platform | قبل از S0 | flag پیشفرض خاموش، accepted | route جدید با flag off، 404 | off |
| D4 | Product | 2026-09-07 | calendar sync unresolved | provider واقعی وارد release نیست | DECIDE |
| D5 | Data owner | قبل از S2 | deadline دقیقاً 24h و closed، accepted | مرز مساوی 409 و بدون mutation | reject |
۲) Example Map و Behavioral Spec
شش example حداقل لازم:
A1 success: Given owner/CONFIRMED/free slot, When valid request, Then 200،
UTC start/end، version+1 و دقیقاً یک RESCHEDULED audit.
A2 same-tenant non-owner: Then 403 APPOINTMENT_NOT_OWNED، بدون PII/mutation.
A3 cross-tenant: Then 404 APPOINTMENT_NOT_FOUND، بدون افشای وجود appointment.
A4 closed boundary: دقیقاً 24h مانده، Then 409 RESCHEDULE_WINDOW_CLOSED،
بدون mutation/audit.
A5 invalid local time: Then canonical 422 INVALID_LOCAL_TIME، بدون write؛ DST ambiguity DECIDE.
A6 concurrent same slot: از دو request همزمان، دقیقاً یک 200 و یک
409 SLOT_CONFLICT، یک interval و یک audit.
A7 replay: همان key و fingerprint، همان response durable و یک audit؛
fingerprint متفاوت، 409 IDEMPOTENCY_KEY_REUSED.Behavioral Spec v1.0: scope شامل reschedule برای appointment confirmed، actor مالک و همان tenant است. preconditionهای مشترک credential معتبر، flag on، appointment موجود و transaction store سالماند. ruleهای بالا status/body، mutation و audit را دقیق میکنند. NFRها شامل privacy-safe logs، p95 نمونهٔ release در S5، idempotency durable و compatibility route قدیمیاند. non-goalها cancel، refund، recurring، تغییر doctor، provider واقعی، reminder delivery، historical backfill و حذف route قدیمیاند. Compatibility یعنی contract قدیمی بدون flag تغییر نکند و schema بهصورت additive expand شود.
۳) Technical Design و ADR
خلاصهٔ design: UI زمان local و timezone را میگیرد؛ API هویت را از credential استخراج و contract را validate میکند؛ service eligibility را پیش از protected write بررسی میکند؛ repository با transaction/constraint رقابت را میبندد؛ idempotency store fingerprint و response durable را نگه میدارد؛ audit writer فقط پس از success مینویسد؛ error serializer پیامهای canonical و privacy-safe میدهد. provider خارجی در این نسخه fake است.
ADR-021 تصمیمهای زیر را تثبیت میکند: S0 با flag off route را 404 میکند؛ S2 مالک barrier و protected write است؛ S3 مالک idempotency است؛ migrationها expand و backward-compatible هستند؛ rollback flag را خاموش میکند و schema، historical audit و idempotency records را حذف نمیکند. اگر DST، calendar provider یا policy deadline تغییر کند، ADR تازه و DECIDE لازم است.
۴) Repository/context map و Ownership plan
apps/web/features/appointments/reschedule-form UI + browser evidence
apps/api/src/modules/appointments route/service/auth/error map
packages/contracts response and compatibility
packages/database transaction/migration/audit
packages/test-fixtures tenant, barrier, replay data
apps/web/tests/e2e real UI→API path
docs/decisions, docs/specs, docs/plans ADR, spec, ExecPlan/evidence
.github/workflows CI gatesOwnership plan: Product owner scope/D1/D4؛ Security reviewer D2، privacy، secret و tenant gate؛ API owner S0/S1/S2؛ Data owner migration و transaction S2؛ Platform owner S3/S5؛ Web owner UI/S1؛ Integration owner merge order و fresh verification. هر owner reviewer مستقل و handoff artifact دارد. مسیر `packages/contracts` shared است؛ تغییرش نیازمند contract review و approval Integration است. Agent هیچ owner تصمیمی نیست.
Permission profile: read-only discovery در ابتدا؛ write فقط مسیرهای Slice؛ network deny؛ fake provider؛ no production/staging endpoint؛ no real credential؛ command allowlist شامل status، diff، targeted test و local fixture؛ deploy، migration destructive، تغییر acceptance و secret access human approval میخواهند.
۵) risk-ranked S0 تا S6 و ExecPlan
S0 (risk: contract/exposure): depends Spec؛ contract+flag+route؛
test flag-off 404 و old route؛ evidence diff/contract output؛ rollback flag off.
S1 (risk: auth/write): depends S0؛ UI→API→DB success؛ test A1/audit/version؛
evidence integration trace؛ recovery flag off و compatible code.
S2 (risk: race/high): depends S1؛ eligibility+barrier/protected write؛
test A4-A6 با one-200/one-409؛ evidence fresh DB race log؛ recovery preserve schema.
S3 (risk: replay): depends S1,S2؛ key/fingerprint/store؛ test A7؛
evidence durable response/audit count؛ recovery retain records.
S4 (risk: privacy): depends S1؛ same/cross tenant و log guard؛ test A2/A3؛
evidence redacted log scan؛ recovery flag off، policy bypass ممنوع.
S5 (risk: compatibility/operability): depends S0-S4؛ CI matrix/benchmark؛
evidence full checks و limitation؛ recovery no threshold editing.
S6 (risk: rollout): depends همهٔ evidence و consumer inventory؛ cohort/flag؛
evidence runbook و rollback rehearsal؛ recovery disable cohort, preserve data.ExecPlan هر task را به یک اجرا محدود میکند: owner و dependency، allowed paths، forbidden paths، command، test oracle، evidence filename و recovery/stop rule. مثلاً task S2-03 فقط fixture barrier و test A6 است؛ فرمان local با database `booking_s2_<run-id>` و network deny؛ pass یعنی invariant کامل و fail یعنی `BLOCKED`، نه حذف assertion. Human ابتدا acceptance و test را تأیید میکند، بعد Agent patch میدهد و verifier تازه آن را اجرا میکند.
## آزمایشگاه
**ورودی:** brief بالا، snapshot فرضی، قرارداد فصلهای ۴ تا ۶ و commandهای read-only. ابتدا این را اجرا کن:
git status --short
rg -n "appointment|reschedule|tenant|actor|timezone|migration" apps packages docs
git diff --check**خروجی مورد انتظار:** inventory مسیرها، baseline فرمانها، جدول facts/decisions، Glossary، Decision Ledger، Example Map با حداقل شش example، Spec با scope و NFR/compatibility، non-goals، Technical Design، ADR، repository/context map، Ownership plan، permission profile، S0 تا S6 و ExecPlan. هیچ فایل production تغییر نکند.
**Recovery:** اگر path با snapshot سازگار نیست، حدس نزن؛ `BLOCKED` و path مشاهدهشده را ثبت کن. اگر stakeholder policy را تعیین نکرده، `DECIDE` با owner و deadline بده. اگر baseline fail است، آن را قبل از Red آینده ثبت و از failure موجود جدا کن.
## Verification
- [ ] هر fact با path/command یا منبع روشن trace میشود.
- [ ] هر decision owner، rationale، status و expiry/revisit دارد.
- [ ] Example Map مرز زمانی، race، idempotency و tenant را پوشش میدهد.
- [ ] Spec actor/trigger/outcome/status/body/mutation/audit را مشخص میکند.
- [ ] non-goalها scope creep را میبندند.
- [ ] ADR migration، flag، compatibility و rollback را توضیح میدهد.
- [ ] Sliceها risk-ranked، وابسته و vertical هستند.
- [ ] ExecPlan allowed/forbidden path، oracle، command و stop rule دارد.
## خطاهای رایج و نشانههای تشخیص
- **fact بهجای decision:** متن میگوید «calendar حتماً release اول است» اما
path یا owner ندارد؛ نشانه، نبود `source` و `deadline` در ledger است.
- **Glossary مبهم:** tenant و actor در چند artifact تعریف متفاوت دارند؛
نشانه، اختلاف نام فیلد یا ورود tenantId از body است.
- **Spec نمایشی:** فقط status موفق آمده؛ نشانه، نبود precondition، zero
mutation برای failure، NFR یا compatibility است.
- **Slice افقی:** itemهایی مثل «همهٔ migrationها» دیده میشود؛ نشانه، نداشتن
actor/outcome/oracle و rollback مستقل است.
- **Ownership پوشاندهشده:** owner «Agent» یا «تیم» نوشته شده؛ نشانه، reviewer،
handoff و permission profile نامعلوم است.
- **ExecPlan غیرقابلاجرا:** command، path مجاز یا stop rule ندارد؛ نشانه،
promptی است که فقط «همهٔ تستها را سبز کن» میگوید.
- **Snapshot بهعنوان حقیقت:** issue comment بر ADR غلبه کرده؛ نشانه، تغییر
policy بدون DECIDE و منبع قابلردگیری است.
## تمرین و Rubric
یک brief تازه برای «لغو و refund» را تحلیل کن، اما fee و provider را خودت اختراع نکن. Rubric: ۲۰٪ facts/decisions؛ ۲۰٪ ledger و unresolved owner؛ ۲۰٪ example/spec/non-goal؛ ۲۰٪ ADR و Slice؛ ۲۰٪ ExecPlan و recovery. تصمیمی که source fact را با policy آینده قاطی کند، blocking failure است.
## جمعبندی
Grilling تولید سؤال زیاد نیست؛ تبدیل ابهام پرریسک به تصمیم قابلردگیری است. Snapshot راهنمای مشاهده است، نه منبع policy. Artifact خوب اجازه میدهد Agent یک Slice باریک را بسازد و انسان بتواند علت، oracle و rollback آن را توضیح دهد.
## منابع مستقیم
- [ISO/IEC/IEEE 29148](https://www.iso.org/standard/72089.html) — مهندسی نیازمندی؛ این درس آن را به artifactهای آموزشی تبدیل میکند.
- [Example Mapping](https://cucumber.io/blog/bdd/example-mapping-introduction/) — ساختار rule/example/question.
- [Codex ExecPlans](https://developers.openai.com/cookbook/articles/codex_exec_plans) — الگوی plan اجرایی؛ قرارداد این فصل مستقل از ابزار است.
- [GitHub Spec Kit](https://github.com/github/spec-kit/) — ابزار/روش spec؛ تضمین policy دامنه نیست.
به زبان ساده
Capstone از یک brief مبهم شروع میکند و باید آن را به Facts، Decision ledger، Example Map، Behavioral Spec، ADR، Slice map و ExecPlan تبدیل کند. Artifact خوب policy را از مشاهده جدا و برای هر task owner، Oracle و stop rule تعیین میکند.
مثال واقعی
در capstone artifact دوره، `00` تا `03` بستهٔ Evidence شامل brief/facts، Spec/non-goals، ADR و ExecPlan را trace میکنند؛ اینها باید به snapshot و تصمیمهای ثبتشده وصل باشند، نه به ادعای شفاهی Agent.