{"id":"analytical-method-validation","name":"analytical-method-validation","summary":"管理フレームワークのもとで、分析手順の検証、検証、文書化を計画、実行、文書化します。これはICH Q2(R2)およびQ14、USP <1220>/<1225>/<1226>、ICH M10生物分析、CLSI EP、またはISO/IEC 17025に基づくものです。","body":"# Analytical Method Validation\n\n## When to use\n\nAny time the question is whether an analytical procedure is fit for its intended purpose:\ndesigning a validation study, evaluating validation data, verifying a compendial procedure,\ntransferring a procedure to another laboratory or instrument, or defending any of these in a\nreport.\n\n## The two rules\n\n**1. Establish which framework governs before designing anything.** The same assay validates\ndifferently under ICH Q2(R2), USP <1225>, ICH M10, CLSI EP, and ISO/IEC 17025. They differ in\nwhich characteristics are required, how the studies are laid out, and whether numeric acceptance\ncriteria are supplied at all. Blending them produces a protocol that satisfies none of them.\n\n**2. State acceptance criteria before collecting data.** Criteria chosen after seeing results are\nnot acceptance criteria, and deciding them post hoc is a standing audit finding. ICH Q2(R2)\ndeliberately supplies almost no numeric criteria — they have to come from the specification, the\nanalytical target profile (ICH Q14 section 3), or development data. ICH M10 is the exception: it\nsupplies explicit numbers, and they differ between chromatographic assays and ligand binding\nassays.\n\n## Scope\n\nThis skill plans studies, computes the statistics correctly, and structures the documentation. It\ndoes **not** decide that a procedure is validated, release a batch, accept or reject a run, close\nan investigation, or substitute for the analyst, the technical reviewer, the quality unit, or the\nregulator. Every script reports; none of them concludes.\n\n## Copyright boundary\n\nICH guidelines are published openly and licensed for reuse with acknowledgement, so their\nrequirements are encoded directly in this skill. **USP general chapters, CLSI EP documents, and\nISO standards are copyrighted and paywalled.** For those, this skill supplies the designation,\nscope, and where to obtain an authorised copy — never the text, never invented thresholds. Do not\nask an agent to retrieve, transcribe, or reconstruct their content. If a number matters and it\nlives in a paywalled document, read it from the authorised copy.\n\n## Frameworks\n\n```bash\ncd skills/analytical-method-validation/scripts\npython3 plan_validation.py --list-frameworks\n```\n\n| Key | Governs | Numeric criteria supplied |\n| --- | --- | --- |\n| `ich-q2r2` | Release and stability testing of drug substances and products | Almost none — you derive them |\n| `ich-m10` | Bioanalytical concentration measurement (PK, TK, BE) | Yes, and they differ by modality |\n| `usp-1220` | Compendial procedure lifecycle, three stages | Paywalled |\n| `usp-1225` / `usp-1226` | Validation / verification of compendial procedures | Paywalled |\n| `clsi` | Clinical laboratory measurement procedures (EP series) | Paywalled |\n| `iso-17025` | Lab-developed and modified methods under accreditation | No — \"to the extent necessary\" |\n\n**Q2(R2) replaced Q2(R1) in November 2023 and restructured the characteristics.** Range is now\nthe parent characteristic (section 3.2), containing *response* (linearity) and *validation of\nlower range limits* (DL/QL). Accuracy and precision are section 3.3 and may be evaluated in\ncombination against a single criterion. Robustness is treated as a development activity and\ncross-refers to ICH Q14. Multivariate procedures are addressed explicitly (2.5 and 3.2.2.3), and\nAnnex 2 adds worked examples for techniques Q2(R1) never covered — quantitative ¹H-NMR, NIR,\nquantitative LC/MS, qPCR, biological assays, and particle size. A Q2(R1)-shaped protocol — a flat\nlist of linearity, range, accuracy, precision, specificity, LOD, LOQ, robustness — is out of date.\nNote also the error correction dated 30 November 2023 to Table 5 and Tables 6–11.\n\n## Scripts\n\n```bash\ncd skills/analytical-method-validation/scripts\n```\n\n| Script | Question answered |\n| --- | --- |\n| `plan_validation.py` | Which framework, which characteristics, what study layout, what protocol? |\n| `check_response.py` | Does the calibration model actually hold across the range? |\n| `check_accuracy_precision.py` | What is the recovery, and how much of the variability is between days? |\n| `check_detection_limits.py` | What are DL and QL by each allowed approach, and do they serve the reporting threshold? |\n| `check_bioanalytical_run.py` | Does this run meet ICH M10 for its modality? |\n| `compare_methods.py` | Are two procedures equivalent, at a pre-stated margin? |\n\nAll take `--format table|tsv|json`. Provenance, guideline citations, and caveats go to stderr;\ndata goes to stdout, so `> out.tsv` keeps them separate. Exit code is `0` for no findings, `1`\nwhen findings were raised, `2` for bad input — so any of them can gate a workflow.\n\n## Workflow\n\n### 1. Fix the framework and the required characteristics\n\n```bash\npython3 plan_validation.py --framework ich-q2r2 --attribute assay --technique hplc --range-use assay\n```\n\nQ2(R2) Table 1 decides what is required from the *measured attribute*, not from the technique. For\nan assay: specificity, response, accuracy, repeatability, intermediate precision. For a limit\ntest: specificity and DL only. For an identity test: specificity alone. Attributes accepted include\n`assay`, `impurity` (quantitative), `impurity-limit`, and `identity`.\n\nReportable range comes from the specification. Q2(R2) Table 2 gives worked examples — 80–120% of\ndeclared content for an assay, 70–130% for content uniformity, reporting threshold to 120% of the\nspecification for an impurity.\n\n### 2. Generate the protocol and fill in the criteria\n\n```bash\npython3 plan_validation.py --framework ich-q2r2 --attribute impurity --protocol > protocol.md\n```\n\nEvery bracketed field is a decision to make and record *before* data collection. The protocol\nskeleton deliberately refuses to pre-fill acceptance criteria for Q2(R2) work, because there is no\ndefensible default.\n\n### 3. Evaluate the response\n\n```bash\npython3 check_response.py -i calibration.csv --max-back-calc-error 2\n```\n\nInput is `level,response`, one row per injection; repeated rows at the same level are replicates,\nand supplying them is what makes the linearity test possible.\n\nReal output from a curve that a coefficient of determination would wave through:\n\n```\nstatistic                           value\ndistinct levels                     5\nslope                               166.6000\nintercept                           2495.0000\nintercept CI includes 0             no\ncoefficient of determination (r2)   0.9830\nlack-of-fit F                       469.5294\nlack-of-fit p                       1.5139e-06\nruns test p                         0.0492\n\nlevel     n  mean_response  mean_back_calculated  relative_error_pct\n50.0000   2  10075.0000     45.4982               -9.0036\n75.0000   2  15150.0000     75.9604               1.2805\n100.0000  2  20050.0000     105.3721              5.3721\n125.0000  2  24050.0000     129.3818              3.5054\n150.0000  2  26450.0000     143.7875              -4.1417\n```\n\nr² = 0.983 and the model is unusable: −9.0% back-calculated error at the bottom of the range,\nlack-of-fit p = 1.5 × 10⁻⁶, non-random residual signs. **r² is not evidence of linearity** — it\nrises with range and is nearly insensitive to curvature. The lack-of-fit F test against pure error\nand the residual pattern are the evidence, which is why Q2(R2) 3.2.2.1 asks for an analysis of the\ndeviation of points from the line rather than a correlation coefficient alone.\n\nAdd `--weight 1/x2` for a wide-range curve. The script flags heteroscedasticity when the residual\nvariance in the top third of the range exceeds the bottom third by more than 10×, because an\nunweighted fit then biases exactly the low end where a reporting threshold lives.\n\n### 4. Evaluate accuracy and precision\n\n```bash\npython3 check_accuracy_precision.py -i ap.csv --accuracy-limit 2 --rsd-limit 1.0 --design-check assay\n```\n\nInput is `level,measured,group`, where `group` is the intermediate-precision factor — day, analyst,\nor instrument.\n\n```\nlevel  component                       sd      rsd_pct  df      ci90_low_sd  ci90_high_sd\n100    repeatability (within group)    0.0707  0.0707   3       0.0438       0.2065\n100    between-group                   1.6515  1.6515   2       n/a          n/a\n100    intermediate precision (total)  1.6530  1.6530   2.0037  0.9554       7.2821\n```\n\nRepeatability of 0.07% RSD looks superb; intermediate precision is 1.65%, twenty-three times\nlarger, because the variability lives entirely between days. Reporting the within-day figure as\nthe procedure's precision would understate routine performance by more than an order of magnitude.\nThis is why the script fits a one-way random-effects model rather than pooling.\n\nTwo traps the script handles for you:\n\n- **Precision is estimated within each level, never pooled across levels.** Pooling 80/100/120%\n  results into one standard deviation turns the range itself into apparent imprecision. The script\n  reports per level, plus a level-independent view as percent of nominal.\n- **`--require-ci-within-limit`** enforces that the whole confidence interval sits inside the\n  limit, not just the mean. Q2(R2) 3.3.1.4 asks for the interval to be *compatible with* the\n  criterion; a mean that scrapes inside on six replicates has not demonstrated much.\n\n### 5. Establish DL and QL, and confirm them\n\n```bash\npython3 check_detection_limits.py --calibration lowcal.csv --blanks blanks.csv \\\n    --confirm-ql 0.05 --confirm-data ql_check.csv --reporting-threshold 0.05\n```\n\n```\napproach                                          sigma   slope      DL      QL\nsd-and-slope (sigma = residual SD of regression)  7.2816  5033.3490  0.0048  0.0145\nsd-and-slope (sigma = SD of y-intercept)          4.3303  5033.3490  0.0028  0.0086\nsd-and-slope (sigma = SD of 8 blanks)             3.7702  5033.3490  0.0025  0.0075\n```\n\nThe same data give QL estimates spanning 1.9×, purely from the choice of σ. Q2(R2) 3.2.3.5\ntherefore requires the limit **and the approach used to determine it** to be reported, and an\nestimated limit to be confirmed with samples at or near it. For an impurity procedure the QL must\nbe at or below the reporting threshold. Reaching for `3.3σ/slope` reflexively, reporting one number\nwith no named approach, and never confirming it are three separate findings.\n\n### 6. Bioanalytical runs under ICH M10\n\n```bash\npython3 check_bioanalytical_run.py --modality chromatographic --run run1.csv\npython3 check_bioanalytical_run.py --modality lba --isr isr.csv\npython3 check_bioanalytical_run.py --modality lba --criteria\n```\n\n`--modality` is mandatory and has no default, because the criteria genuinely differ:\n\n| | Chromatographic | Ligand binding assay |\n| --- | --- | --- |\n| Calibration tolerance | ±15%, ±20% at LLOQ | ±20%, ±25% at LLOQ and ULOQ |\n| Accuracy / precision | ±15% / ≤15% CV (±20% / ≤20% at LLOQ) | ±20% / ≤20% CV (±25% / ≤25% at LLOQ and ULOQ) |\n| A&P design | 4 QC levels, 5 replicates/run, ≥3 runs over ≥2 days | 5 QC levels, 3 replicates/run, ≥6 runs over ≥2 days |\n| Total error | no such criterion | ≤30%, ≤40% at LLOQ and ULOQ |\n| ISR agreement | ±20% for ≥2/3 of repeats | ±30% for ≥2/3 of repeats |\n\nApplying the ±15% chromatographic numbers to a ligand binding assay, or importing the LBA total-error\ncriterion into a chromatographic method, are both common and both wrong.\n\nThe run check enforces the per-level rule that gets missed: at least 2/3 of *all* QCs **and** at\nleast 50% at *each* level. A run can pass the overall fraction while a single level fails\ncompletely.\n\n```\nfinding: QC level high: 0/2 within tolerance (0%); M10 requires at least 50% at each level\n```\n\n### 7. Transfer and method comparison\n\n```bash\npython3 compare_methods.py -i paired.csv --margin 2 --relative --slope-tolerance 0.05\n```\n\n```\nmean difference (%)                       1.4646\nTOST margin                               2.0000\nTOST p-value                              1.0528e-13\n90% CI (TOST)                             1.44127 to 1.48797\nequivalent at stated margin               yes\n--- for contrast only ---\npaired t-test p (NOT equivalence)         0.0000\nOLS slope (biased here)                   1.0396\nDeming slope                              1.0398\nPassing-Bablok slope                      1.0351\n```\n\nTwo errors this replaces:\n\n- **\"p > 0.05, no significant difference, therefore the methods are equivalent.\"** Failing to\n  detect a difference is not evidence of equivalence, and on a small transfer dataset that outcome\n  is close to guaranteed. TOST tests the hypothesis that matters — that the true difference lies\n  inside a pre-stated margin. Here the t test says the difference is highly significant *and* TOST\n  says the methods are equivalent at ±2%; both are true, and only one answers the question.\n- **Ordinary least squares for method comparison.** OLS assumes the reference values carry no\n  error, which is false when comparing two procedures, and biases the slope toward zero. Deming\n  (with a stated error-variance ratio) and Passing–Bablok (non-parametric, outlier-resistant) are\n  the appropriate regressions and are reported side by side with OLS for contrast.\n\nThe script also flags proportional bias — when the difference trends with concentration, a single\nmean bias and its limits of agreement are misleading regardless of how tight they look.\n\n## What this skill exists to prevent\n\n1. Validating against ICH Q2(R1)'s structure three years after Q2(R2) replaced it.\n2. Acceptance criteria written after the data were seen.\n3. r² presented as evidence of linearity.\n4. Repeatability reported as the procedure's precision, with the between-day component invisible.\n5. One DL/QL number with no named approach and no confirmation.\n6. Chromatographic M10 criteria applied to a ligand binding assay, or the reverse.\n7. A t test's non-significance presented as equivalence at a method transfer.\n\n## References\n\n- `references/framework-selection.md` — which framework governs, and the questions that decide it\n- `references/ich-q2r2.md` — structure, Table 1 and Table 2, per-characteristic recommended data\n- `references/ich-m10-bioanalytical.md` — the full chromatographic and LBA criteria side by side\n- `references/compendial-and-clsi.md` — USP, CLSI and ISO designations, scope, and how to cite them\n- `references/statistics.md` — the statistical methods, why each one, and the common errors\n- `references/source-ledger.md` — provenance and research dates for every claim in this skill\n\n## Assets\n\n- `assets/validation-protocol-template.md` — protocol structure with criteria stated up front\n- `assets/validation-report-template.md` — report structure with raw-data traceability","author":"@K-Dense-AI","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/K-Dense-AI/scientific-agent-skills/tree/main/skills/analytical-method-validation","license":"MIT","category":"document","lang":"en","tokens":3744,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"assets/validation-protocol-template.md","size":4021,"sha256":"5b1d613ec3c32cdb36d48ce6e55986f313aeedda3fdccbde435126c8bba5ae8d"},{"path":"assets/validation-report-template.md","size":3945,"sha256":"ae81ac9943cc6a06d8bf581a32f8d78745ed6ec9dbf9b9c6d51a416bbc9bcfa1"},{"path":"references/compendial-and-clsi.md","size":7442,"sha256":"8825d6390cc96fe28437c83548811a1ae0dddbdc534c263d5d20e92951c7108d"},{"path":"references/framework-selection.md","size":5250,"sha256":"05b07ce6206bdddee9225fa410d333232b7618bfaf99cf237d80ae3dffcd9564"},{"path":"references/ich-m10-bioanalytical.md","size":7324,"sha256":"bb25701919290249ac63674dbf07a9f14b8dee7d8f3fcc266abd5b34325548ce"},{"path":"references/ich-q2r2.md","size":13596,"sha256":"255797c9cd0326250f3f20b59b827d71f174d89253359226b2e5917d160ef94d"},{"path":"references/source-ledger.md","size":7888,"sha256":"79b326dc761642a7d4312cd92b70fca908039db43c58209d0d44514f38137a9b"},{"path":"references/statistics.md","size":11370,"sha256":"973a3eb2eef2ceda3024f0afcccfd160b8d8ee923288521421a533bc262b2c7b"},{"path":"scripts/_catalog.py","size":23838,"sha256":"dd750f2f283c238875e1927cf4f08f9dc21ff9c2acacf8c5316a49a30887b857"},{"path":"scripts/check_accuracy_precision.py","size":14263,"sha256":"05b4751505bccf3953762b569aecf8371208173aa216fe3501c84ceb56d453df"},{"path":"scripts/check_bioanalytical_run.py","size":11344,"sha256":"c16398b2ee77bddebbfbf84f1c9abe9a9045be40a7c9108972e3ca16f3d26751"},{"path":"scripts/check_detection_limits.py","size":13215,"sha256":"58e4387324b1884e7963f4b006fa4405da4d5f1ad587efdbba43f695d1ddb4ca"},{"path":"scripts/check_response.py","size":9858,"sha256":"068fe65932f389c1aba56689e5924eb57016421fcfaee0f024344b6e3ecbbeb5"},{"path":"scripts/_common.py","size":32420,"sha256":"a251b81f6240ac61b4f2ff2be9c644bf41ccca39e4ad462d9abd740c36b2f4ee"},{"path":"scripts/compare_methods.py","size":9105,"sha256":"54f5a7cdbccac073128a7df8a5519b04d0e215498f7542cba8a93832d6a09f1b"},{"path":"scripts/plan_validation.py","size":15414,"sha256":"8c47e921d13a0e521669790a820be2f1caf0492538229615104aef4e7c389fe0"}],"requires":{"mcp":[],"tools":["Read Write Edit Bash"]},"safety":{"flags":[{"code":"net.endpoints","kind":"exfiltration","excerpt":"clsi.org, database.ich.org, doi.usp.org, www.iso.org, www.uspnf.com","message":"bundled scripts reach 5 external host(s)","severity":"warn"}],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":["clsi.org","database.ich.org","doi.usp.org","www.iso.org","www.uspnf.com"]}}