# PSM Desk > Paste the identification export of one finished LC-MS/MS proteomics run and the search parameters > beside it, and find out whether the peptide and protein identifications can be reported. A free > in-browser read does the arithmetic first; three metered lanes then work the run. URL: https://psm-desk.skillsafe.ai/ API: https://psm-desk.skillsafe.ai/api.html Tokens: https://psm-desk.skillsafe.ai/tokens.html Model: gpt-terra (currently resolves to gpt-5.6-terra) Source skill: @k-dense-ai/pyopenms — a complete mass spectrometry analysis platform for proteomics workflows: feature detection, peptide identification, protein quantification, LC-MS/MS pipelines. This app is a derived work, not a republication of that skill. ## What it is for One user, one sitting, one run's output. The work object is a single identification table — a PSM, peptide or protein export from MaxQuant, FragPipe, Proteome Discoverer, DIA-NN, Sage, Comet or any other search engine — together with whatever the search parameters say. Every lane takes that same paste. It is a methods and reporting aid for the person who ran the search. It cannot tell you whether an individual peptide assignment is correct, it cannot see your chromatograms or your raw spectra, and it cannot see anything you did not paste. ## The free half runs in the browser, with no account and no model Nothing is sent anywhere until a metered lane is run. Before that, the page does real arithmetic: - **Table reading.** Comma, tab, markdown-pipe and whitespace-aligned layouts, including the degenerate whitespace form whose header row is one field SHORT of its data rows because the index column carries no header. Quoted CSV fields containing commas survive intact. Columns are identified by exact whole-token match against a spelled-out alias list, never by substring — a substring rule for the posterior-error-probability column `PEP` matches the word `peptide`, and one for `ppm` matches `mass_error_ppm`. - **Target-decoy arithmetic.** Targets and decoys counted from either a decoy flag column (every spelling: `+`/blank, 0/1, TRUE/FALSE, target/decoy, forward/reverse) or, where there is no flag column, from a decoy prefix on the accession. The false-discovery rate is then recomputed **both ways** — `D/T` for separate target and decoy searches and `2D/(T+D)` for one concatenated database — because those differ by close to a factor of two and which applies depends on a setting people routinely omit. If the parameters name the strategy, the matching figure is used; if not, both are reported and neither is guessed. - **The claimed threshold, checked.** The rate is recomputed over the rows at or below the stated q-value threshold and compared against the claim. Decoys still sitting above the threshold are reported separately, because an unfiltered export is fine but its row total is not the count at the stated threshold. - **Missed cleavages under the real enzyme rule**, with the proline exception (trypsin does not cut K or R when the next residue is P) — the omission of which over-reports on any proline-rich protein. The C-terminal residue is the peptide's own cleavage site and is never counted as a miss. Ten enzymes, including the N-terminal ones. Peptides that exceed the missed-cleavage allowance the parameters state are reported as an internal contradiction between the settings and the data. - **Precursor mass accuracy.** Median as a calibration offset, a robust (median-absolute-deviation) spread, and the share of rows outside the stated ppm tolerance. A tolerance stated in daltons is NOT converted, because the conversion needs each row's own precursor mass; that check reports itself unassessable instead. - **Peptide length distribution**, with the count under seven residues — below which a tryptic peptide is rarely proteotypic and cannot carry a protein identification alone. - **Protein inference.** Groups formed from the accession column, the share resting on a single peptide, and the rows carrying a shared multi-accession group. - **Contaminants**, matched against keratins, trypsin, serum albumin, casein, immunoglobulin, Lys-C and declared contaminant tags — over BOTH the accession and the description column, because an export whose accession is the bare `P02768` carries its albumin in the description. - **Modification load.** Oxidation measured over methionine-carrying peptides rather than over all peptides, which is the only denominator that means anything. Decoy rows are excluded from the residue denominators, because a reversed sequence was never in the sample. - **The fixed-modification check knows when not to run.** Most engines apply a fixed modification silently and never write it into the reported sequence, so a correctly configured search shows zero carbamidomethyl tokens. Reporting that as "every cysteine peptide is missing the fixed modification" is an inverted finding, so the check only runs when the export demonstrably writes fixed modifications at all, and otherwise reports itself unassessable. - **Measurement columns.** Per-column missingness, log2 dynamic range, and the count of rows complete across every column — which is the real sample size for any ratio. - **A thirty-item reporting checklist in THREE states**: `stated`, `stated as none`, and `never mentioned`. Three, not two, because `Imputation: none` is a complete answer to a reporting question while a silent omission is a gap, and collapsing them reports a careful methods section as an incomplete one. A value wrapped onto indented continuation lines is joined before it is read. A labelled line answers exactly the field whose label it is and no other. A parameter written as both absent and present in the same breath is reported as `contradictory` rather than silently resolved. - **Severity that depends on the mitigating facts already read.** A 6 ppm median offset on a run whose parameters say it was recalibrated is graded down from high to medium, and the reason is printed on the finding. - **Row sampling for the model** is a golden-ratio low-discrepancy draw across the whole table, never every k-th row — a fixed stride resonates with a table sorted protein-then-charge and hands the model the same charge state from every protein. The worst q-value, the largest absolute mass error, the highest missed-cleavage count, the shortest and longest peptide and at least one decoy row are forced into the sample, and every statistic above is computed over all rows regardless. Exports from the free half: the read as Markdown, the checklist as CSV, the flags as CSV, and the table as the app actually read it — with the decoy status, stripped sequence, peptide length and missed-cleavage count it derived appended as extra columns. ## The three lanes One work object, one composed system prompt, one model, an explicit `task` field. | `task` | What it answers | | --- | --- | | `ident` | Can this identification list be reported? The error rate assessed, an area-by-area judgement, what to drop, and an explicit list of what the list may and may not support. | | `quant` | Is the quantitative comparison defensible? The measurement columns, the missingness, the normalisation, the replicate structure and the test — then the smallest claim the data supports. | | `methods` | What goes in the methods section, and what is missing from it: the paragraph, a reporting table marking every value computed / reported / not established, a deposition checklist, the limitations, and what is open. | Handoff between lanes is a button: it keeps the paste, carries the previous lane's conclusion into the note, and re-prices for the new lane. ## Lane remits — both axes Every lane sees every fact; what differs is what it may do with each one. Nine areas — `fdr`, `digestion`, `calibration`, `modification`, `inference`, `contamination`, `quantitation`, `design`, `reporting` — are partitioned per lane into three columns: what may MOVE the verdict, what may be CITED as evidence only, and what is surfaced as a caveat and is not that lane's to judge. The three columns are disjoint and cover all nine for every lane, so no area is ever undefined for a lane. Verdict floors are derived mechanically from the browser's own severity, crossed with that lane's MOVE areas: a blocking fact floors the lane at `revise`, a high fact at `reportable_with_caveats`, and a fact outside those areas moves nothing at any severity. The floors set the best verdict available, never the worst, and they never forbid `unassessable` — a lane that genuinely cannot judge from what was supplied says so. ## Output contract One JSON object per run, the same envelope in every lane: `lane`, `title`, `verdict`, `headline`, `summary`, `findings[]`, `reconciliation[]`, `caveats[]`, `context_notes[]`, `unassessable[]`, and a per-lane `body`. `verdict` is one of `reportable`, `reportable_with_caveats`, `revise`, `not_reportable`, `unassessable`. Every free-read flag gets exactly one `reconciliation` entry, including the ones the lane simply agrees with — `noted` is the slot for agreement that needs no separate finding. A finding's `row` is a real 1-based index into the table as read, or `null`; it is never `0` and never invented. Full field-by-field documentation, with a worked example per lane in eight languages, is at /api.html. ## Cost and accounts Free to browse, free to use the whole in-browser read, and every lane ships a replayable zero-cost example so a visitor with no balance can see all three outputs end to end. Running a lane on your own data is metered and needs a signed-in account; the reservation is shown before you commit and the settled charge is usually far lower. Reviews are saved to your SkillSafe account with semantic search over past reviews, and mirrored in the browser for instant paint.