بازگشت به کتابخانهکتابخانه8.2Skill قابل‌حمل و Hookهای deterministic برای Format، Guard و Audit
مهندسی نرم‌افزار ایجنت‌محور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 8.2فصل ۸ — ساخت هارنس — Rules، Skills، Hooks و MCP

Skill قابل‌حمل و Hookهای deterministic برای Format، Guard و Audit

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

راهنمایی می‌گوید چه کاری مطلوب است؛ enforcement می‌گوید چه کاری اجازهٔ عبور ندارد. یک Skill خوب چرخهٔ TDD را قابل‌تکرار می‌کند، اما به‌تنهایی جلوی `rm` یا تغییر assertion را نمی‌گیرد. Hookهای deterministic باید قبل از tool call و بعد از edit، ورودی و خروجی قابل‌ممیزی بسازند: format، guard و audit. این جداسازی برای `booking-platform` حیاتی است، چون یک جملهٔ خوب نمی‌تواند جای permission یا sandbox را بگیرد.

## اهداف

پس از این درس می‌توانی Skill چندمرحله‌ای بنویسی؛ pre-tool guard و post-edit formatter/auditor را از prompt جدا کنی؛ تفاوت guidance و enforcement را توضیح دهی؛ و برای هر کنترل تصمیم fail-open یا fail-closed را با ریسک و recovery توجیه کنی.

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

درس ۲۸ محل ذخیرهٔ راهنما را انتخاب کرد. اکنون در مرز `Implement → Observe → Verify`، همان قرارداد را به workflow و gate تبدیل می‌کنیم. تست A-04 باید اول Red دیده شود؛ Agent نباید با تغییر expected آن را سبز کند؛ و هیچ hookی نباید deadline یا tenant policy را به‌طور مخفی تغییر دهد.

## Skill چیست؟

یک Skill portable معمولاً metadata کوتاه، trigger یا ورودی روشن، مراحل، خروجی، stop condition، ابزارهای مجاز و verification دارد. مراحل را به سه سطح تقسیم کن: **discovery** برای baseline و authority؛ **execution** برای یک تغییر محدود؛ **verification** برای تست و diff. جزئیات syntax هر ابزار در adapter می‌رود. Skill نباید ادعا کند خودش sandbox است یا approval را دور می‌زند.

مثال بد

وقتی هر کاری خواستی انجام دادی، formatter را اجرا کن و اگر تست fail شد
assertion را با مقدار واقعی هماهنگ کن تا CI سبز شود.

این guidance مبهم است، هدف را به خروجی تبدیل می‌کند و test oracle را قربانی می‌کند. همچنین «هر کاری» scope و rollback ندارد.

مثال خوب: `tdd-reschedule`

Input: acceptance A-04، baseline revision، مسیر policy و test.
1. Read-only: authority، command و baseline را ثبت کن.
2. Test author: تست جدید را بدون implementation اجرا و Red را ثبت کند.
3. Implementer: فقط policy و فایل production مجاز را تغییر دهد.
4. Verify: targeted test، typecheck و diff test/production را بررسی کن.
5. Stop: ambiguity، network، migration، credential یا تغییر oracle.
Output: Red/Green log، diff، limitation و recovery.

این متن guidance است؛ enforcement را Hook و permission profile انجام می‌دهند.

## سه Hook و دو سیاست خطا

**Pre-tool guard** پیش از اجرای command، executable و argument را parse می‌کند. `pnpm exec vitest run ...` در allowlist می‌تواند عبور کند؛ `deploy`, `curl` به endpoint تولید، `git push` و هر حذف بازگشت‌ناپذیر block یا approval می‌خواهند. Guard نباید با regex ساده‌ای که quote و path traversal را نمی‌فهمد به‌عنوان sandbox جا زده شود.

**Post-edit format** پس از تغییر فایل‌های TypeScript، formatter را در workspace ایزوله و بدون network اجرا می‌کند. اگر formatter fail شد، diff را نگه می‌داریم و status را failure می‌کنیم؛ formatter نباید خودش assertion را عوض کند.

**Post-edit audit** diff را برای path خارج از allowlist، تغییر test oracle، credential pattern، production URL و تغییر permission می‌خواند و report می‌دهد. این audit باید line/path/evidence داشته باشد، نه فقط «امن است».

**Fail-closed** یعنی در خطای hook، عمل متوقف می‌شود. برای production command، secret، migration destructive، permission و تغییر acceptance مناسب است؛ availability کمتر ولی ریسک false negative پایین‌تر است.

**Fail-open** یعنی اگر hook در دسترس نبود، عمل ادامه می‌یابد. برای یک lint تزئینی یا telemetry کم‌اهمیت ممکن است پذیرفتنی باشد، اما باید visible warning، محدودیت scope و owner داشته باشد. fail-open برای guard امنیتی ادعای حفاظت کاذب می‌سازد. در هر دو حالت timeout، crash، bypass و version mismatch را test کن.

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

Skill فقط S2 policy را می‌سازد. Pre-tool اجازهٔ خواندن `packages/contracts` و اجرای Vitest را می‌دهد؛ network و database واقعی deny است. Post-edit audit اگر `apps/api` یا migration تغییر کرد، fail-closed می‌کند و وضعیت `DECIDE` می‌سازد. formatter در fixture محلی مجاز است. اگر audit service unavailable شد، patch merge نمی‌شود؛ recovery شامل ذخیرهٔ diff، ثبت revision و اجرای audit از یک runner trusted است.

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

**ورودی:** Skill متن بالا، دو فایل policy/test، commandهای مجاز و یک fixture بدون secret واقعی.

۱. جدول policy بساز: action، risk، hook، on-success، on-failure. برای `vitest`، `prettier --check` و `git push` مقدار بده.

۲. پیش از tool call، command را بدون اجرا parse کن و فقط allowlist را قبول کن. تست‌های بی‌خطر:

ALLOW  pnpm exec vitest run packages/contracts/src/reschedule-policy.test.ts
ALLOW  pnpm exec prettier --check packages/contracts/src/reschedule-policy.ts
BLOCK  git push origin main
BLOCK  curl https://production.invalid/health

`production.invalid` صرفاً یک مقدار نمونهٔ غیرواقعی برای آموزش است و endpoint واقعی نیست.

۳. دو edit مصنوعی بساز: یکی فقط rename محلی، دیگری تغییر `expect(result.status)` و افزودن `EXAMPLE_TOKEN_DO_NOT_USE`. audit اول را pass و دومی را fail-closed کند.

**خروجی مورد انتظار:** JSON یا log شامل event، path، revision، decision، reason و recovery؛ هیچ command شبکه یا destructive اجرا نشده باشد. **Recovery:** اگر parser نتوانست command را با اطمینان تحلیل کند، deny و درخواست بررسی انسانی بده؛ اگر formatter خروجی متفاوت داد، format را ثبت کن و oracle را دست نزن.

## خطاهای رایج

  • پنهان کردن enforcement داخل متن Skill و تصور اینکه مدل همیشه obey می‌کند.
  • اجرای formatter با network یا روی production workspace.
  • audit پس از merge، وقتی برگشت هزینه‌بر شده است.
  • fail-open برای guard و گزارش آن به‌عنوان security control.
  • hookی که output را تغییر می‌دهد ولی diff یا log قابل‌ردیابی ندارد.
  • match کردن فقط نام command و نادیده گرفتن argument، cwd، symlink و environment.

## Verification

  • [ ] Skill ورودی، مراحل، stop، خروجی و recovery دارد.
  • [ ] pre-tool قبل از اجرا تصمیم می‌دهد و command مبهم را deny می‌کند.
  • [ ] post-edit format و audit deterministic و قابل تکرارند.
  • [ ] تغییر assertion، credential و path خارج از scope detectable است.
  • [ ] fail-closed برای کنترل‌های امنیتی انتخاب و دلیلش ثبت شده است.
  • [ ] hook خودش trusted و version-pinned است و bypass آن قابل مشاهده است.

برای جلوگیری از false confidence، hookها را با fixtureهای مثبت و منفی version-control کن. یک command معتبر باید در دو اجرای مستقل همان تصمیم را بدهد؛ یک command با quoting متفاوت، symlink یا argument اضافه نباید بدون بررسی عبور کند. همچنین hook باید event id، revision و نسخهٔ parser را ثبت کند تا بعداً معلوم باشد کدام policy تصمیم گرفته است. اگر hook به service خارجی وابسته است، آن dependency خودش surface حمله و نقطهٔ fail است؛ برای gate امنیتی، اجرای محلی و deterministic یا runner مورد اعتماد بهتر از call مبهم بیرونی است.

guidance و enforcement را در گزارش جدا بنویس. مثلاً Skill می‌گوید «تست را پیش از implementation اجرا کن»؛ pre-tool guard می‌تواند command ممنوع را متوقف کند؛ اما هیچ‌کدام به‌تنهایی ثابت نمی‌کنند Red به علت درست رخ داده است. شاهد Red باید از test output و baseline بیاید و شاهد audit باید از diff. این separation به reviewer اجازه می‌دهد یک کنترل ناقص را با ادعای کنترل کامل اشتباه نگیرد.

## تمرین و پاسخ نمونه

برای hookی که پیش از `pnpm exec vitest` اجرا می‌شود، تعیین کن اگر binary پیدا نشد یا parser timeout کرد چه رخ دهد.

**پاسخ نمونه:** چون hook بخشی از gate تست و درستی شواهد است، هر دو حالت fail-closed: command اجرا نمی‌شود، diff و error در audit ثبت، و recovery اجرای همان revision در runner trusted است. اگر hook فقط telemetry بود، fail-open با warning ممکن بود. معیار پاسخ باید تفاوت safety property و convenience check، idempotence و نبود secret/network باشد.

## جمع‌بندی

Skill روش کار را portable می‌کند، اما hook enforcement محدود و قابل‌آزمون می‌دهد. قبل از tool call guard کن، بعد از edit format و audit کن، و fail-open را فقط برای کنترل غیرامنیتی با warning بپذیر. شکست یا دورزدن hook باید evidence تولید کند، نه سکوت.

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

  • [Agent Skills specification](https://agentskills.io/specification) — ساختار Skill؛ execution و enforcement را تضمین نمی‌کند.
  • [Cursor Hooks](https://cursor.com/docs/hooks.md) — schema و lifecycle محصول؛ version و surface را در adapter ثبت کنید.
  • [Claude Code hooks guide](https://code.claude.com/docs/en/hooks-guide) — رویدادها و کنترل‌های hook؛ جای sandbox OS نیست.
  • [NIST SSDF](https://csrc.nist.gov/pubs/sp/800/218/final) — کنترل‌های توسعهٔ امن و شواهد؛ الگوی hook خاصی الزام نمی‌کند.
  • [OWASP Secure Coding Practices](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/) — محدودیت اعتبارسنجی و اجرای command؛ راهنمای عمومی است.

به زبان ساده

Skill می‌گوید چه مراحلی مطلوب‌اند، اما Hook و permission باید واقعاً عمل خطرناک را کنترل کنند. Guard پیش از tool call و audit پس از edit می‌توانند با ریسک مشخص fail-closed یا در کنترل کم‌اهمیت fail-open شوند.

مثال واقعی

در آزمایش فرضی دوره، اجرای Vitest مجاز است اما `git push` و curl به endpoint تولید block می‌شوند؛ تغییر matcher از دقیق به `toBeDefined()` باید در audit دیده شود.

دانش‌سنجی

آزمون درس

۵ Q
01
کدام جزء Skill قابل‌حمل ضروری است؟
02
وظیفهٔ pre-tool guard چیست؟
03
برای guard امنیتی که parser آن crash کرده، کدام سیاست مناسب‌تر است؟
04
کدام edit باید post-edit audit را متوقف کند؟
05
فرمت کردن کد چه محدودیتی دارد؟

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

برای Skill `tdd-reschedule`، pre-tool guard و post-edit audit طراحی کن و برای سه خطای hook recovery بده.