# SustainScore A production-readiness score that separates demo-ware from software that is ready to hand over, with the rubric's caps enforced in code. SustainScore encodes a five-dimension readiness rubric (security, bugs, compliance, consistency, usability) with six bands from Prototype to Mature and a table of hard caps. You supply the scorer's raw judgement per dimension, the defects that are currently open, and the evidence. It returns scores that apply the rubric's anti-inflation rules mechanically. ## Why A rubric kept as prose gets applied charitably. A scorer can write down, in the same row, that an open defect caps Security at 45 and still record 70. Nothing in a document stops that. Encoding the rubric does. The worked example in `examples/cap-by-worst-defect.ts` scores a fictional product whose security review notes an exposed API key that was never rotated. Run it with `npx tsx examples/cap-by-worst-defect.ts`: ``` Hand-scored vs rubric-enforced security raw 70 -> 45 Beta <- CAPPED by sec.leaked-secret (45) bugs raw 68 -> 68 Launch-capable, with debt compliance raw 73 -> 73 Launch-capable, with debt consistency raw 68 -> 68 Launch-capable, with debt usability raw 68 -> 68 Launch-capable, with debt hand-scored overall : ~69 enforced overall : 45 (Beta) weakest : security average : 64.4 (reported, never the headline) delta : -24 ``` A 24-point overstatement and a band change, from one cap a human reader skipped. ## What it enforces | Rule | Enforcement | |---|---| | Cap by the worst defect | The lowest open cap on a dimension wins. One serious hole is not averaged away by ten strengths. | | Evidence required above 70 | A score above 70 with no cited evidence is held at 70 and reported as NOT ASSESSED, not as a pass. Unverified is treated as absent. | | Overall is the lowest dimension | `overall` is the minimum. The average is reported alongside it and labelled so it cannot be read as the headline. | | Suite caps apply before averaging | A shared platform flaw hits every product. Averaging first would let healthy products dilute it. | An unknown cap id throws. Dropping a mistyped cap without a word would inflate the score, which is the failure this library prevents. A cap assigned to the wrong dimension also throws, and so does a product with a missing dimension. Caps can cite what establishes them: `openCaps` takes a bare id or `{ id, evidence }`. A cap that cites nothing is still applied, and the report says it cited nothing. An unproven cap is a smaller error than an unapplied one. The full cap table is exported as `CAP_RULES`, the bands as `BANDS`. ## Usage ```ts import { scoreProduct, scoreSuite, formatProduct } from '@extant2000/sustain-score' const result = scoreProduct({ product: 'example-app', tier: 'A', dimensions: { security: { raw: 70, openCaps: ['sec.leaked-secret'], evidence: [{ ref: 'docs/security-review.md:14' }] }, bugs: { raw: 68, evidence: [{ ref: 'test/api.test.ts' }] }, compliance: { raw: 73, evidence: [{ ref: 'docs/compliance-checklist.md' }] }, consistency: { raw: 68, evidence: [{ ref: 'shared/ui/' }] }, usability: { raw: 68, evidence: [{ ref: 'reports/accessibility-scan.json' }] }, }, }) result.overall // 45, the lowest dimension result.weakest // 'security' result.band.name // 'Beta' console.log(formatProduct(result)) ``` `scoreSuite(products, { suiteCaps })` rolls several products up with tier weighting (A = 2, B = 1.5, C = 1). `productReport()` and `suiteReport()` return a `CapabilityReport` from `@extant2000/evidence-record`, and `formatProduct()` and `formatSuite()` render one. Evidence can be the evidence-record shape or a plain `{ ref, note }`, where a `path:line` ref becomes a file citation. To rank several scored products by what to fix next, see `@extant2000/portfolio-advisor`. ## Install The package is published to the GitLab package registry, so point the `@extant2000` scope there first: ``` npm config set @extant2000:registry https://gitlab.com/api/v4/packages/npm/ npm install @extant2000/sustain-score ``` ## License MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).