«API را بساز، migration بزن و test بنویس» برنامه نیست. اجراکننده نمیداند کدام API، چه migration سازگاری میخواهد، test چه outcomeی دارد یا failure کجا باید تصمیمگیری شود. ExecPlan باید بدون transcript قابل اجرا باشد: مسیر، فرمان، dependency، evidence، recovery و rollback را برای یک رفتار مشخص نگه میدارد.
## اهداف یادگیری
پس از این درس میتوانی ExecPlan خودبسندهٔ reschedule با taskهای ثابت P1 تا P6 بنویسی، graph وابستگی آن را بخوانی، تناقض میان Spec/design/task/test را repair کنی، و gate پیش از اجرا و merge را با evidence واقعی اعمال کنی.
## جای درس در چرخهٔ سرتاسری
Spec v1.0 درس ۱۳، R-01 تا R-06 و A-01 تا A-12 را پذیرفته است. Design درس ۱۴ mechanism اتمی، migration سازگار و ADR-021 را تعریف میکند. این درس گذار Specify به Test و Implement است: هیچ requirement تازهای نمیسازد؛ فقط کار لازم برای تحقق همان Spec را ترتیب، قابل اجرا و قابل بررسی میکند.
## ExecPlan خودبسنده: Reschedule v1.0
Artifact زیر کامل است؛ P1 تا P6 تنها identifierهای برنامهاند. همهٔ pathها متعلق به booking-platform فرضی این دورهاند و commandها در root همان مخزن اجرا میشوند.
# ExecPlan: conflict-safe appointment reschedule v1.0
Status: Ready after P1 evidence
## Authority and baseline
Canonical behavior: docs/specs/reschedule.md, version 1.0, R-01 to R-06 and A-01 to A-12.
Architecture decision: docs/decisions/adr-021-database-interval-invariant.md, Accepted.
Repository map: docs/maps/appointments.md; facts must be revalidated at the current commit.
Scope: owner reschedules a confirmed appointment for the same doctor in the same tenant.
Non-goals: refund, cancellation, recurring appointments, doctor change, provider sync, notification, historical backfill.
Accepted decisions: clinic timezone is authoritative; adjacent intervals are allowed; exact 24-hour deadline is closed.
Safety: no production credentials; use TEST_DATABASE_URL only; stop on a non-ephemeral database.
## P1 — baseline, authority, and discovery
Depends on: none.
Paths: docs/specs/reschedule.md; docs/decisions/adr-021-database-interval-invariant.md;
docs/maps/appointments.md; package.json.
Work: confirm all R/A identifiers, accepted decisions, existing migration convention, and test commands.
Commands:
git rev-parse HEAD
git status --short
rg -n "reschedule|Idempotency-Key|appointments" apps/api packages docs
Expected evidence: one commit SHA; clean or explicitly scoped working tree; paths for route, repository,
migration runner, and tests recorded in docs/maps/appointments.md.
Recovery: missing or contradictory fact means stop; update map or return to the owning Spec/ADR, never infer it.
Rollback: none; P1 is read-only.
## P2 — contract and red acceptance tests
Depends on: P1.
Paths: packages/contracts/src/appointments/reschedule.ts;
apps/api/src/modules/appointments/reschedule.route.ts;
apps/api/src/modules/appointments/reschedule.int.test.ts;
apps/api/src/config/feature-flags.ts.
Work: create `RESCHEDULE_V1_ENABLED` in feature-flags.ts with default false; tests set it true.
Define the endpoint, request schema, success body, and every error code in Spec v1.0;
write tests A-01 through A-07 and A-09 through A-12 before implementation.
Commands:
pnpm exec vitest run apps/api/src/modules/appointments/reschedule.int.test.ts
pnpm exec tsc --noEmit
Expected evidence: before P4, named tests fail only because endpoint behavior is absent; the default flag returns 404
and test configuration with RESCHEDULE_V1_ENABLED=true reaches the route; schema typecheck passes.
Recovery: a failing assertion not traceable to A-01 through A-12 is removed or the Spec is revised by its owner.
Rollback: revert only P2 paths; no persistent data has changed.
## P3 — persistence invariant and compatible migration
Depends on: P1 and ADR-021.
Paths: packages/database/migrations/20260905_add_reschedule_idempotency.sql;
packages/database/src/appointments/reschedule-repository.ts;
packages/database/src/appointments/appointment-schema.ts.
Work: enable `btree_gist`; add `EXCLUDE USING gist (tenant_id WITH =, doctor_id WITH =,
tstzrange(start_at_utc,end_at_utc,'[)') WITH &&) WHERE (status = 'confirmed')`.
In one PostgreSQL transaction, insert/read the idempotency row, `SELECT id FROM appointments WHERE id = $1 FOR UPDATE` the target,
update it, insert audit, and commit. Map SQLSTATE 23P01 to `SLOT_CONFLICT`; map idempotency
unique conflict to `REQUEST_IN_PROGRESS`; P4 maps both to their Spec v1.0 409 bodies.
Commands:
psql "$TEST_DATABASE_URL" -v ON_ERROR_STOP=1 -f packages/database/migrations/20260905_add_reschedule_idempotency.sql
pnpm exec vitest run apps/api/src/modules/appointments/reschedule.int.test.ts
Expected evidence: migration exits 0 on an ephemeral test database; repository test can persist one protected interval;
raw constraint details do not escape the repository result.
Recovery: unsupported constraint, lock, or non-ephemeral URL stops work; record engine/version in
docs/maps/appointments.md and reopen ADR-021 with the database owner.
Rollback: set `RESCHEDULE_V1_ENABLED=false` in `apps/api/.env.staging`, reload API configuration, and deploy
the prior compatible code; retain new tables/indexes and records. Do not drop schema after writes.
Repair is a reviewed forward migration.
## P4 — endpoint, transition, idempotency, and telemetry
Depends on: P2 and P3.
Paths: apps/api/src/modules/appointments/reschedule.route.ts;
apps/api/src/modules/appointments/reschedule.service.ts;
apps/api/src/modules/appointments/reschedule-telemetry.ts;
apps/api/src/config/feature-flags.ts.
Work: derive tenant/actor from authentication; validate R-01/R-02; execute the P3 protected write;
implement R-04/R-05 response replay and safe outcome/latency telemetry.
Commands:
pnpm exec vitest run apps/api/src/modules/appointments/reschedule.int.test.ts
pnpm exec eslint apps/api/src/modules/appointments/reschedule.route.ts apps/api/src/modules/appointments/reschedule.service.ts
Expected evidence: A-01 through A-07 and A-09 through A-12 pass; logs contain correlationId and key hash,
never raw key, patient fixture name, SQL, or stack trace.
Recovery: unknown in-progress behavior or a new error outcome freezes P4 and requires Spec v1.1 approval.
Rollback: set `RESCHEDULE_V1_ENABLED=false` in `apps/api/.env.staging` and reload API configuration;
prior compatible code ignores the added idempotency storage.
## P5 — concurrent integration evidence
Depends on: P4.
Paths: apps/api/src/modules/appointments/reschedule.int.test.ts;
packages/test-fixtures/src/appointments/reschedule-fixture.ts.
Work: implement A-08 with a deterministic barrier that releases two requests for one interval simultaneously;
execute 100 iterations and assert one 200, one 409 SLOT_CONFLICT, one confirmed interval, one audit event.
Commands:
pnpm exec vitest run apps/api/src/modules/appointments/reschedule.int.test.ts --testNamePattern="A-08"
pnpm exec vitest run apps/api/src/modules/appointments/reschedule.int.test.ts --testNamePattern="A-08" --repeat=100
Expected evidence: both commands exit 0; 100 iterations contain no double success and no second audit event.
Recovery: flaky ordering means repair fixture barrier or transaction boundary; do not add sleep or retry-until-green.
Rollback: revert P5 test/fixture only; a P5 failure blocks merge and leaves P4 disabled.
## P6 — consistency review and evidence pack
Depends on: P2, P3, P4, and P5.
Paths: docs/specs/reschedule.md; docs/decisions/adr-021-database-interval-invariant.md;
docs/plans/reschedule-execplan.md; apps/api/src/modules/appointments/reschedule.int.test.ts;
apps/web/tests/e2e/reschedule.spec.ts; apps/api/src/modules/appointments/reschedule.latency.bench.ts;
packages/test-fixtures/src/appointments/reschedule-latency-fixture.ts.
Work: scan R-01 to R-06 against model, tasks, tests, diff, privacy logs, compatibility, and non-goals.
P6 creates both `reschedule.latency.bench.ts` and `reschedule-latency-fixture.ts` from the complete
NFR-01 schema: before each sample, seed/reset appointment `appt-latency-001` with
`tenantId:"tenant-latency-a"`, `patientId:"patient-latency-a"`, `status:"confirmed"`, and
`existingRevision:41`; set `auth.actorId:"patient-latency-a"` equal to `patientId:"patient-latency-a"`.
Send America/New_York, 11:00-11:30 local, and key `latency-a09-n`
for sample n; require response revision 42. Then run 100 samples at concurrency one and 400ms p95.
Commands:
rg -n "refund|notification|recurring|doctor change" docs/specs/reschedule.md apps/api packages apps/web
pnpm exec vitest run apps/api/src/modules/appointments/reschedule.int.test.ts
pnpm exec playwright test apps/web/tests/e2e/reschedule.spec.ts
case "${TEST_DATABASE_URL:-}" in postgresql://*localhost*booking_platform_bench_*|postgres://*localhost*booking_platform_bench_*|postgresql://*127.0.0.1*booking_platform_bench_*|postgres://*127.0.0.1*booking_platform_bench_*) ;; *) echo "use only a local disposable booking_platform_bench database"; exit 1;; esac
RESCHEDULE_V1_ENABLED=true pnpm exec tsx apps/api/src/modules/appointments/reschedule.latency.bench.ts --database "$TEST_DATABASE_URL" --fixture packages/test-fixtures/src/appointments/reschedule-latency-fixture.ts --samples 100 --concurrency 1
git diff --check
Expected acceptance observation: scope scan finds no reschedule refund/notification implementation; integration/E2E checks exit 0;
benchmark prints `environment=local-disposable git_revision=fc6c40b samples=100 concurrency=1 observed_response_revision=42 expected_revision_transition=41->42 p95_server_duration_ms=372 threshold_ms=400 result=PASS`;
whitespace check prints nothing; Spec-to-test matrix covers every R/A identifier. This is expected
benchmark output, not a claim that the fictional staging runner was executed.
Recovery: `seed_revision_mismatch` or `response_revision_mismatch` exits 1; reset fixture seed, verify P3 migration
and route flag, then rerun only on a new local disposable database. Any other contradiction returns to its owner: behavior to Spec,
mechanism to ADR/design, task to ExecPlan, assertion to test. No assertion is weakened solely to pass a gate.
Rollback: set `RESCHEDULE_V1_ENABLED=false` in `apps/api/.env.staging`, reload API configuration, and use
P3 compatible-schema rollback limits; preserve audit/idempotency evidence.این برنامه artifactهای project را نام میبرد، نه folderهای خیالی یا command «کشفشده». P1 تنها جای discovery است و خروجی آن fact versioned میسازد. P2 testهای رفتار را پیش از implementation آماده میکند. P3 فقط persistence است؛ P4 endpoint و replay را اضافه میکند؛ P5 race را اثبات میکند؛ P6 evidence را جمع و scope را بازبینی میکند.
## Task Graph همان ExecPlan است
P1 baseline, authority, discovery
├── P2 contract and red acceptance tests
└── P3 persistence invariant and compatible migration
P2 + P3 ──> P4 endpoint, transition, idempotency, telemetry
│
└── P5 concurrent integration evidence
P2 + P3 + P4 + P5 ──> P6 consistency review and evidence packP2 و P3 پس از P1 فقط وقتی موازیاند که P3 schema و P2 test fixture shared state نداشته باشند؛ در غیر این صورت P2 سپس P3 اجرا میشوند. P4 به هر دو وابسته است. P5 و P6 هرگز پیش از P4 تمام نیستند. Worktree فقط checkout را ایزوله میکند؛ `TEST_DATABASE_URL`، migration و fixture همزمان باید namespace جدا یا execution ترتیبی داشته باشند.
## contradiction و incomplete scan
برای هر R/A، چهار ستون را مقایسه کنید: Spec، design/model، P-task و test. نمونهٔ fail: Spec میگوید refund non-goal، ولی P4 queue refund، schema `refund_status` یا A-09 انتظار refund دارد. repair این نیست که test را حذف کنیم: Product باید Scope revision را تأیید کند، یا P4/schema/test با non-goal هماهنگ حذف شوند. نمونهٔ incomplete: R-05 هست اما P3 storage، P4 replay و A-10/A-11 ندارد؛ پس P3/P4/P2 ناقصاند.
## مثال ضعیف و علت شکست
P1 API را بساز، P2 database را تغییر بده، P3 test را اجرا کن.شناسهها فقط ترتیب ظاهریاند؛ path، behavior، command، expected output، recovery و rollback ندارند. graph نمیگوید P2 بر schema یا P3 بر endpoint وابسته است، بنابراین Agent میتواند migration را روی database اشتباه اجرا کند یا test سبز غیرمرتبط را evidence بداند.
مثال اصلاحشده
P3 در این plan نام، path، dependency، SQL command، expected migration output، stop condition و rollback limit دارد؛ P5 همان A-08 را با invariant دقیق اثبات میکند؛ P6 تناقض refund را به owner artifact بازمیگرداند. هیچ identifier بیرون از P1 تا P6 معنای دیگری ندارد.
## آزمایشگاه: plan را gate کن
**ورودی:** Spec v1.0، ADR-021 و ExecPlan بالا. **گامها:** ۱) P1 evidence را ثبت کن. ۲) R/Aها را کنار P2 تا P6 trace کن. ۳) graph را برای shared DB بررسی کن. ۴) scan refund و idempotency را اجرا کن. ۵) commandهای P2، P5 و P6 را فقط روی محیط امن اجرا کن.
rg -n "R-0[1-6]|A-(0[1-9]|1[0-2])|P[1-6]" docs/specs/reschedule.md docs/plans/reschedule-execplan.md**خروجی مورد انتظار:** هر identifier R/A و P در artifact مربوط match میشود؛ P5 تنها task race و P6 تنها task evidence-pack است. **Recovery:** اگر مسیر یا command P1 وجود ندارد، P1 را failed ثبت کن و هیچ task بعدی را اجرا نکن. اگر graph cycle دارد، dependency مبهم را به owner design برگردان.
## خطاهای رایج و نشانهها
- **شناسهٔ سرگردان:** P5 در quiz یا graph معنایی متفاوت از ExecPlan دارد.
- **دستور غیرقابل اجرا:** مقدار نمونهٔ غیرواقعی یا command بدون path/environment نوشته شده است.
- **evidence مبهم:** «تست سبز» بدون suite، outcome و iteration است.
- **rollback تزئینی:** schema نوشتهشده یا audit ثبتشده را با revert code قابل حذف میداند.
- **scan سطحی:** فقط واژهٔ refund را مییابد اما owner و اثر Scope را بررسی نمیکند.
## چکلیست Verification
- [ ] P1 تا P6 دقیقاً یک نام، dependency و outcome دارند.
- [ ] هر path، command، output، recovery و rollback حد دارد.
- [ ] P1 تنها discovery و P6 تنها evidence-pack است.
- [ ] A-08 با barrier و 100 iteration، نه sleep، بررسی میشود.
- [ ] scan Spec/design/task/test refund و R-05 را پوشش میدهد.
- [ ] merge بدون P6 evidence یا با route flag روشن پس از failure انجام نمیشود.
## تمرین مستقل
برای reminder idempotent، ExecPlan پنجtaskی با identifierهای ثابت، retention و scan opt-out بنویس.
**Rubric پاسخ:** هر task path/command/output/recovery دارد؛ graph queue و storage dependency را روشن میکند؛ retry همان پیام را دوبار نمیفرستد؛ opt-out authority در Spec است؛ migration rollback، پیام ارسالشده را undo خیالی نمینامد؛ و review identifierها را در plan/test یکسان میبیند.
## جمعبندی
ExecPlan خودبسنده، کار را از امید به قرارداد اجرایی تبدیل میکند. P1 تا P6 discovery، contract، persistence، endpoint، proof race و review را بدون renumbering به هم متصل میکنند. Graph shared state را آشکار میسازد و scan تناقض/ناقصبودن جلوی Scope creep یا evidence جعلی را میگیرد.
## منابع مستقیم
- [OpenAI Codex — ExecPlans](https://developers.openai.com/cookbook/articles/codex_exec_plans) — الگوی رسمی plan اجرایی طولانی؛ استاندارد عام مهندسی نیست.
- [GitHub Spec Kit](https://github.com/github/spec-kit/) — نمونهٔ زنجیره Spec/Plan/Tasks؛ details ابزار نسخهپذیرند.
- [Martin Fowler — Understanding SDD](https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html) — تحلیل تنوع جریانهای SDD.
- [Michael Nygard — Documenting Architecture Decisions](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions) — تفکیک تصمیم پایدار از progress/log.
به زبان ساده
ExecPlan باید بدون transcript قابل اجرا باشد: هر task مسیر، dependency، فرمان، Oracle، recovery و rollback خودش را دارد. Graph وابستگی نشان میدهد چرا مثلاً proof رقابت بعد از protected write میآید.
مثال واقعی
در artifact فرضی دوره، P1 کشف read-only، P2 قرارداد و تست Red، P3 invariant پایگاهداده، P4 endpoint، P5 تست همزمانی و P6 بستهٔ شواهد است؛ هر شناسه یک معنی ثابت دارد.