Files
sustain-score/README.md
T
2026-09-28 14:49:03 -04:00

105 lines
4.0 KiB
Markdown

# 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 exact 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
```
npm install @extant2000/sustain-score
```
## License
MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).