First public release
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# PortfolioAdvisor
|
||||
|
||||
Rank a portfolio of capabilities by transition readiness, and say what would
|
||||
move each one.
|
||||
|
||||
PortfolioAdvisor takes a list of scored capabilities and returns them ordered
|
||||
by the best next move. It is designed to consume
|
||||
`@extant2000/sustain-score` output, but it takes a plain shape, so any source
|
||||
of scores can feed it. It is pure, with no clock and no I/O.
|
||||
|
||||
## What it is built against
|
||||
|
||||
A bad ranking is not the main risk. The risk is a ranking that reads as
|
||||
authoritative when the numbers behind it are not comparable. There are three
|
||||
ways that happens.
|
||||
|
||||
1. Unassessed capabilities sort as bad. A capability nobody scored lands at
|
||||
the bottom next to one that was scored and found weak. Those are opposite
|
||||
situations: one needs work, the other needs an assessment. A single
|
||||
ordered list cannot hold both, so unassessed capabilities are listed
|
||||
separately and never interleaved. A `null` score is not zero.
|
||||
2. A composite hides its binding constraint. "Reports: 6" tells you where
|
||||
something sits, not what to do. Every entry carries its binding
|
||||
constraint and next action. An entry with neither is reported as "a
|
||||
position, not advice".
|
||||
3. Effort is invisible in a score. Two capabilities at 45, one needing a key
|
||||
rotation and one needing a rewrite, are not equally worth doing next.
|
||||
|
||||
So the ranking is by readiness gained per unit of effort, not by score.
|
||||
Each entry gets a `leverage` value:
|
||||
|
||||
```
|
||||
(100 - score) × weight ÷ effort
|
||||
```
|
||||
|
||||
That is the headroom left, scaled by how much the capability matters, divided
|
||||
by what the work costs. Effort weights are `trivial` 1, `small` 2, `medium` 4
|
||||
and `large` 8. `unknown` counts as `medium`, not `trivial`. An unestimated
|
||||
task is not a cheap one, and defaulting optimistically is how an unscoped item
|
||||
reaches the top of a plan. Only a capability scoring 90 or more passes; the
|
||||
rest are reported as work outstanding.
|
||||
|
||||
## Two orders
|
||||
|
||||
`formatRanking()` prints the ranked table first and then the findings
|
||||
worst-first. The two answer different questions ("what should I do next" and
|
||||
"what is most wrong"), and they are not the same order. A ranking whose
|
||||
display order contradicts its own numbering is worse than no numbering, so the
|
||||
table always prints #1 first.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { rank, rankingTable, formatRanking, type CapabilityStanding } from '@extant2000/portfolio-advisor'
|
||||
|
||||
const portfolio: CapabilityStanding[] = [
|
||||
{ name: 'docs', score: 90, effort: 'small', bindingConstraint: 'none material' },
|
||||
{ name: 'gateway', score: 85, effort: 'small', bindingConstraint: 'retry fix unmerged', nextAction: 'merge the retry fix' },
|
||||
{ name: 'reports', score: 60, effort: 'large', bindingConstraint: 'scheduled exports promised, nothing runs them', nextAction: 'build the scheduler' },
|
||||
{ name: 'billing', score: 80, effort: 'trivial', bindingConstraint: 'debug logging left on', nextAction: 'turn off debug logging' },
|
||||
]
|
||||
|
||||
const { ranked, unassessed } = rank(portfolio)
|
||||
console.log(rankingTable(portfolio))
|
||||
|
||||
// Table plus the evidence-record findings
|
||||
console.log(formatRanking(portfolio))
|
||||
```
|
||||
|
||||
`toReport(portfolio)` returns a `CapabilityReport` from
|
||||
`@extant2000/evidence-record`. Each entry can also carry a `weight` (default
|
||||
1) and a `where` that is cited as the source of the score.
|
||||
|
||||
## Sample output
|
||||
|
||||
Produced by running `rankingTable()` on the example above:
|
||||
|
||||
```
|
||||
#1 billing 80/100 leverage 20 turn off debug logging
|
||||
#2 gateway 85/100 leverage 7.5 merge the retry fix
|
||||
#3 reports 60/100 leverage 5 build the scheduler
|
||||
#4 docs 90/100 leverage 5 none material
|
||||
```
|
||||
|
||||
The weakest capability is third. Ordering by score alone would have put the
|
||||
large rebuild ahead of a change that is close to free. A capability with
|
||||
`score: null` would appear below the table, unnumbered, as unassessed.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
npm install @extant2000/portfolio-advisor
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).
|
||||
Reference in New Issue
Block a user