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>
113 lines
3.8 KiB
Markdown
113 lines
3.8 KiB
Markdown
# PivotOps
|
|
|
|
Decide whether a plan still holds when conditions change, and name the
|
|
assumption that broke.
|
|
|
|
PivotOps is a pure function over a plan and its current readings. It has no
|
|
clock and does no I/O, and it executes nothing. It tells you whether to
|
|
proceed, pivot to a fallback, stop, or hold.
|
|
|
|
## What it checks and why
|
|
|
|
Conditions changing is not the real failure. The failure is a plan that never
|
|
wrote down what it assumed, so nobody can tell whether a change matters.
|
|
PivotOps is built against three versions of that.
|
|
|
|
1. A plan whose assumptions are implicit cannot be invalidated. Say a rollout
|
|
assumes a host has disk headroom, a registry is reachable and a
|
|
maintenance window is open. If none of that is recorded, a full disk only
|
|
shows up as the failure at the end. A plan with no recorded assumptions
|
|
returns `hold`, never `proceed`.
|
|
2. A stale reading is worse than no reading. Replanning against a condition
|
|
nobody re-measured gives a confident decision from an old fact. An
|
|
assumption with `observed: null` is NOT ASSESSED and blocks. "We did not
|
|
look" is not "it is fine".
|
|
3. Degradation without a floor is optimism. "Reduce concurrency and continue"
|
|
is a plan. "Continue at reduced capacity" with no point at which capacity
|
|
becomes unacceptable is a way of never stopping. A broken assumption whose
|
|
fallback has no `floor` makes the decision `stop`, not `pivot`.
|
|
|
|
## Four decisions, not two
|
|
|
|
| Decision | When |
|
|
|---|---|
|
|
| `proceed` | every assumption was re-checked and holds |
|
|
| `pivot` | something broke, and each broken assumption has a fallback with a floor |
|
|
| `stop` | something broke with no fallback, or with a fallback that has no floor |
|
|
| `hold` | nothing broke, but at least one condition was never re-measured |
|
|
|
|
`hold` gives "we do not know" its own outcome, separate from "carry on" and
|
|
"abandon". Folding it into either one leads to the two mistakes this library
|
|
exists to prevent: proceeding on unread conditions, or killing a viable
|
|
rollout because one probe was missing. A broken assumption outranks an unread
|
|
one, so `stop` wins over `hold`.
|
|
|
|
Non-numeric values are never ordered. An `at-least` comparison of `"closing"`
|
|
against `"open"` reports NOT ASSESSED instead of comparing the strings, which
|
|
would pass or fail on alphabetical order. `equals` works on any value.
|
|
|
|
## Usage
|
|
|
|
```ts
|
|
import { replan, formatReplan, type Plan } from '@extant2000/pivot-ops'
|
|
|
|
const plan: Plan = {
|
|
name: 'nightly rollout',
|
|
assumptions: [
|
|
{
|
|
id: 'disk-headroom',
|
|
description: 'the host has room for the new image',
|
|
condition: 'disk free bytes',
|
|
comparison: 'at-least',
|
|
required: 5e9,
|
|
observed: 2e9,
|
|
where: 'df -P /var/lib/docker',
|
|
fallback: { description: 'reduce concurrency to 1', floor: 'stop below 500 MB free' },
|
|
},
|
|
{
|
|
id: 'window',
|
|
description: 'the maintenance window is open',
|
|
condition: 'window state',
|
|
comparison: 'equals',
|
|
required: 'open',
|
|
observed: 'open',
|
|
where: 'change calendar',
|
|
},
|
|
],
|
|
}
|
|
|
|
const { decision, reason, broken, unverified } = replan(plan)
|
|
console.log(decision)
|
|
console.log(reason)
|
|
|
|
// The full report, for a terminal or CI log
|
|
console.log(formatReplan(plan))
|
|
```
|
|
|
|
`toReport(plan)` returns the same result as a `CapabilityReport` from
|
|
`@extant2000/evidence-record`, where every finding cites the reading it was
|
|
based on.
|
|
|
|
## Sample output
|
|
|
|
Produced by running the example above and printing `decision` and `reason`:
|
|
|
|
```
|
|
pivot
|
|
1 assumption(s) broken, each with a fallback and a floor: disk-headroom → reduce concurrency to 1.
|
|
```
|
|
|
|
## 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/pivot-ops
|
|
```
|
|
|
|
## License
|
|
|
|
MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).
|