Files
hand-off/README.md
T
Paul HittandClaude Opus 5.5 3f264f72e9 Publish to the GitLab package registry on version tags
The README's install line failed: the package was never published, and
nothing told npm where the @extant2000 scope lives. A tag like v1.2.3 now
publishes to this project's registry with CI_JOB_TOKEN, and the README
sets the scope's registry before installing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:41:56 -04:00

86 lines
3.3 KiB
Markdown

# HandOff
Decides whether a capability can be operated by someone other than the
person who built it, by looking at what it depends on.
## What it does and why
Handover is usually treated as a documentation problem, but documentation is the smaller part of it. **Operability is the absence of dependence on a specific person or
machine.** A thorough runbook does not remove a dependency. It describes one.
HandOff takes the list of things a capability depends on (credentials,
runtimes, knowledge, access, data), what each one is bound to, and what the
recipient would use instead. It checks them for three failures.
1. A personal credential doing institutional work. For example, several
products deploy with one engineer's personal access token. No runbook
transfers that: the recipient cannot obtain it, and it stops working when
the account goes away. It is reported as a critical blocker, not a gap in
the docs.
2. A dependency on a machine instead of a role. A script that hard-codes a
path on one laptop runs on that laptop. It works perfectly, forever, for
one person.
3. A substitute nobody has exercised. This one survives every review,
because it is written down and so looks done. A runbook nobody but its
author has followed is a draft, so it is reported as not assessed.
An account binding with no recorded way for the recipient to get their own
account fails at high severity. Operating knowledge that exists only in
someone's head fails too.
## Readiness is the worst dependency, never the average
One personal credential blocks a transfer however good everything else is.
Averaging is how that blocker disappears behind nine green rows.
| Readiness | When |
|---|---|
| `transferable` | Every dependency is unbound or has a substitute someone else has exercised. |
| `blocked` | At least one dependency cannot be handed over. |
| `unproven` | Nothing is blocked, but at least one substitute has never been exercised. |
| `unknown` | No dependencies were listed. The capability was assumed, not examined. |
## Usage
```ts
import { assess, formatHandOff, type Capability } from '@extant2000/hand-off'
const capability: Capability = {
name: 'release pipeline',
recipient: 'the partner ops team',
dependencies: [{
name: 'deploy-token', kind: 'credential',
coupling: 'person', boundTo: 'alice',
substitute: 'a group deploy token per project',
substituteVerified: true,
where: 'config/deploy.env',
}],
}
const { readiness, reason, blockers, unproven } = assess(capability)
console.log(formatHandOff(capability)) // report in the evidence-record format
```
`substituteVerified` means someone other than the author has followed the
substitute. Leaving it out counts as `false`, because an unexercised
substitute is a claim. The example above is `transferable`; drop the
substitute and it becomes `blocked`.
`coupling` is one of `person`, `machine`, `account` or `none`. `kind` is one
of `credential`, `runtime`, `knowledge`, `access` or `data`. Every finding
cites the dependency's `where`. The library is pure: no clock and no I/O.
## 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/hand-off
```
## License
MIT. See [LICENSE](LICENSE).