امنیت، هویت، استقرار و تابآوری · فصل ۷ — Dependency، Contract، Event و Network Failure · 111
Dependency/Toolchain Mismatch
failure را بعد از وقوع و بهصورت موردی تحلیل میکند و هنوز detection، containment و repair path از قبل طراحی نشده است. موضوع جاری: «Dependency/Toolchain Mismatch». میتواند failure family «Dependency/Toolchain Mismatch» را بازتولید/تشخیص دهد و detection، containment و recovery مناسب را تعیین کند.
خلاصهٔ درس
failure را بعد از وقوع و بهصورت موردی تحلیل میکند و هنوز detection، containment و repair path از قبل طراحی نشده است. موضوع جاری: «Dependency/Toolchain Mismatch».
میتواند failure family «Dependency/Toolchain Mismatch» را بازتولید/تشخیص دهد و detection، containment و recovery مناسب را تعیین کند.
خروجی این درس
میتواند failure family «Dependency/Toolchain Mismatch» را بازتولید/تشخیص دهد و detection، containment و recovery مناسب را تعیین کند.
1. مسئله واقعی چیست؟
دو توسعهدهنده یک checkout ظاهراً یکسان دارند. روی سیستم اول تستها سبز است و روی سیستم دوم همان فرمان با خطای بارگذاری یا ناسازگاری runtime شکست میخورد. خطا ممکن است در انتهای زنجیره دیده شود، اما علتِ قابل دفاع باید از روی شواهد لایهبهلایه پیدا شود. در این درس «Dependency/Toolchain Mismatch» یعنی بین محیط اعلامشده و محیط واقعاً resolve/executeشده اختلافی وجود دارد که میتواند failure را توضیح دهد. هدف حدسزدن از روی متن خطای نهایی نیست؛ هدف ساختن یک زنجیره شواهد بازتولیدپذیر است.
2. محیط را به چند لایه مستقل تقسیم کن
برای تشخیص، حداقل این لایهها را جدا نگه دار: dependencyهای مستقیمِ اعلامشده، درخت dependency واقعاً resolveشده، نسخه و metadata ابزار package manager، runtime یا tool executable، و platform/native boundary. دو سیستم میتوانند package.json یکسان داشته باشند ولی transitive dependency متفاوت resolve کنند. همچنین میتوانند lockfile یکسان داشته باشند ولی با runtime major متفاوت اجرا شوند. اگر این لایهها را در یک برچسب کلی مثل «محیط» ادغام کنی، اولین نقطه واگرایی پنهان میشود.
3. lockfile چه چیزی را ثابت میکند و چه چیزی را نه؟
مستندات رسمی npm، package-lock.json را توصیف دقیق درخت dependency تولیدشده برای کمک به نصبهای تکرارپذیر میداند [NPM-LOCK]. این claim به resolution درخت package محدود است. lockfile بهتنهایی نسخه OS، runtime، compiler، executable واقعی در PATH، native ABI یا سرویس بیرونی را ثابت نمیکند. پس «lockfile یکسان است» یک مشاهده مفید است، نه حکم نهایی درباره یکسانبودن کل toolchain.
4. Environment Fingerprint بساز
قبل از repair یک fingerprint حداقلی ثبت کن: manifest/constraints، شناسه lock یا resolved tree، package-manager name/version، runtime name/version، tool executable identity، platform/architecture marker و هر native-addon marker مرتبط. fingerprint باید observation باشد، نه inference. جمله «احتمالاً Node اشتباه است» observation نیست؛ runtime_major=20 در برابر runtime_major=18 observation است.
5. اول failure را تمیز بازتولید کن
اگر failure فقط یک بار و در محیطی شلوغ دیده شده، causal diagnosis هنوز زود است. fixture را کوچک کن، ورودی و فرمان را ثابت نگه دار و symptom را حداقل دوبار در شرایط تعریفشده بازتولید کن. cache، فایلهای تولیدشده و state باقیمانده باید یا کنترل شوند یا صریحاً بهعنوان unknown ثبت شوند. تا وقتی reproduction پایدار نیست، repairهای متعدد فقط شواهد را آلوده میکنند.
6. اولین divergence قابل توضیح را پیدا کن
از لایههای زودتر به دیرتر حرکت کن. اگر manifest یکسان است ولی resolved tree در یک transitive package واگرا میشود و همان تفاوت با failure همبستگی آزمایشی دارد، لازم نیست runtime را هم فوراً مقصر اعلام کنی. diagnosis در «اولین divergence مشاهدهشدهای که میتواند failure را توضیح دهد» متوقف میشود و لایههای بعدی تا زمان نیاز unproven میمانند. این قاعده ادعا نمیکند هر failure فقط یک علت دارد؛ فقط از overclaim جلوگیری میکند.
7. Transitive Resolution Drift
یکی از failureهای رایج این است که direct dependencyها یکساناند، اما resolved tree متفاوت است. این تفاوت میتواند از lock ناقص، resolution policy متفاوت یا state قدیمی ناشی شود. probe مناسب باید فقط resolution را تغییر دهد و runtime/platform را ثابت نگه دارد. اگر با برگرداندن resolved tree به حالت baseline failure هم ناپدید شود و mutation دوباره آن را برگرداند، causal support قویتر میشود.
8. Runtime و Tool Executable را از package resolution جدا کن
گاهی dependency tree کاملاً یکسان است ولی runtime major یا executable واقعی فرق دارد. مثال: هر دو سیستم packageهای یکسان دارند، اما یکی fixture را با runtime 18 و دیگری با runtime 20 اجرا میکند و fixture عمداً فقط روی 18 معتبر است. در این حالت reinstall packageها repair هدفمند نیست. evidence باید نشان دهد resolved tree ثابت است و divergence در runtime/tool executable رخ داده است.
9. Platform و Native Boundary
یک پروژه JavaScript میتواند dependency tree یکسان داشته باشد اما native addon یا platform marker متفاوتی داشته باشد. architecture، OS، ABI یا binary target میتواند قبل از اجرای منطق برنامه failure ایجاد کند. اگر marker پلتفرم اولین تفاوت قابل توضیح است، repair باید همان boundary را هدف بگیرد؛ تغییر دادن dependencyهای نامرتبط فقط symptom hunting است.
10. هر divergence علت failure نیست
ممکن است package-manager metadata field متفاوت باشد ولی resolved tree، runtime، platform و symptom همه یکسان بمانند. آن اختلاف «واقعی» است اما شواهدی ندارد که failure را توضیح دهد. در این حالت رکورد باید divergence را ثبت کند و causal_support را رد یا OPEN نگه دارد. تشخیص حرفهای بین «چیزی فرق دارد» و «این تفاوت failure را توضیح میدهد» فاصله میگذارد.
11. دام Warm Cache و Green Reinstall
فرض کن reinstall روی محیط failشده سبز میشود چون cache از قبل artifact سازگار دارد. سبزشدن یک rerun ممکن است symptom را پنهان کند، اما causal proof نیست. برای closure باید recurrence probe داشته باشی: mutation را دوباره وارد کن یا cache state را کنترلشده بازسازی کن و ببین sensor همان failure را دوباره میگیرد. اگر نمیتوانی recurrence را نشان دهی، status باید «repair not closed» بماند.
12. Containment با Repair فرق دارد
containment باید reversible و محدود باشد: مثلاً اجرای task روی runtime سازگار، pin موقت یک resolution شناختهشده، یا غیرفعالکردن مسیر native ناسازگار در fixture. repair یعنی علت پشتیبانیشده را در همان boundary اصلاح کنی و سپس مستقل verify کنی. containment برای کاهش اثر است؛ repair برای برگرداندن محیط به state قابل دفاع.
13. Repair Proof چه اجزایی دارد؟
یک repair معتبر حداقل چهار چیز دارد: baseline failure قابل بازتولید، تغییر محدود و مرتبط با divergence، rerun مستقل که symptom را رفع میکند، و recurrence test که با بازگرداندن mutation دوباره failure را میبیند. اگر فقط مرحله سوم وجود دارد، هنوز نمیدانی کدام تغییر مؤثر بوده یا آیا state پنهان دخالت داشته است.
14. Dependency Divergence Diagnosis Record v1
برای هر case جدول را با این هشت بعد پر کن: layer_identity، declared_vs_observed، reproduction، first_divergence، causal_support، containment، repair_rerun و unknowns. observation و inference را جدا بنویس. اگر evidence ناقص است، OPEN را حفظ کن و دقیقاً بگو برای closure چه دادهای لازم است. هدف رکورد، تولید داستان ریشهای نیست؛ هدف ساختن مرز قابل audit برای تصمیم است.
15. Transfer: package manager دیگری، همان استاندارد شواهد
در fixture دوم نام package manager و runtime marker تغییر میکند. نباید conclusionهای npm را کپی کنی. از صفر fingerprint را بساز، resolved state را با runtime/tool state جدا کن و اولین divergence قابل توضیح را پیدا کن. اگر runtime اولین divergence پشتیبانیشده است، همان را ثبت کن حتی اگر metadata ابزار هم متفاوت باشد. مهارت transferable، نام ابزار نیست؛ روش تفکیک evidence و محدودکردن claim است.
ارزیابی
پاسخنامهی این درس در بستهی آموزش وجود ندارد؛ خودت جواب بده و صادقانه علامت بزن. «نیاز به مرور» درس را به صف تکرار میفرستد.
FLR-001-AF1 formative
FLR-001-AF2 formative
FLR-001-AF3 formative
FLR-001-S1 retrieval
FLR-001-S2 application
FLR-001-S3 diagnosis
FLR-001-S4 decision
FLR-001-S5 transfer
ملاکهای ارزیابی (rubric)
| Dimension | Sufficient evidence | Blocking failure |
|---|---|---|
| Reproduction + identity | Fixture/environment identity is explicit and the symptom is reproducible when claimed | Diagnosis starts from an unreproduced final error |
| Evidence discrimination | Declared, resolved, tool/runtime, and platform layers are separate; one alternative is rejected | One artifact such as a lockfile produces complete causal closure |
| First divergence + causal support | The earliest divergence that can explain the symptom is identified and later layers remain bounded | Every observed difference is labeled causal |
| Containment + repair proof | Containment is reversible; repair has independent rerun and recurrence evidence | Green reinstall/rerun alone treated as proof |
| Transfer | Alternate-manager case is rebuilt from changed evidence | npm-specific classification copied without new evidence |
منابع و تازگی
مطالعهٔ موارد (case studies)
Same manifest, different transitive resolved version; runtime/platform held constant.
Package-manager metadata differs but symptom and execution state do not; divergence is non-causal.
Warm cache makes reinstall green; recurrence remains untested.
Different package manager and runtime marker require rebuilding the evidence chain rather than copying npm conclusions.
تصاویر درس
فایلهای آزمایشگاه
Use the eight deterministic synthetic fixtures in fixtures/cases.json.
For each case:
- identify the evidence layer being compared;
- separate declared state from observed/resolved/executed state;
- confirm whether the symptom is reproducible;
- locate the earliest divergence that can explain the failure;
- record negative evidence that weakens at least one alternative;
- choose reversible containment before broader repair;
- require an independent rerun plus recurrence test for repair closure; and
- preserve missing runtime/platform facts as OPEN rather than guessing.
Implement build_record(case) in starter.py. The learner files contain no hidden expected classifications. Everything is local, synthetic, and offline.