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>
3.8 KiB
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.
- 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, neverproceed. - 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: nullis NOT ASSESSED and blocks. "We did not look" is not "it is fine". - 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
floormakes the decisionstop, notpivot.
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
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.