First public release

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-28 14:49:01 -04:00
co-authored by Claude Opus 5.5
commit 9c0198cfd8
14 changed files with 1873 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
+8
View File
@@ -0,0 +1,8 @@
node_modules/
dist/
*.log
.DS_Store
.env*
!.env.example
.npmrc
dist-cjs/
+6
View File
@@ -0,0 +1,6 @@
stages: [test]
test:
stage: test
image: node:22-alpine
script: [npm ci, npm run build, npm test]
+14
View File
@@ -0,0 +1,14 @@
# Changelog
## 0.1.0 - 2026-09-28
First public release under MIT.
- `unify()` and `formatPane()` roll signals from different sources into one
report in the `@extant2000/evidence-record` format.
- Readings older than the freshness window, or with no timestamp, are not
assessed, never green. `now` is injected.
- `ambiguousAttribution()` names subjects a source files under one shared
identity, and their per-subject readings are not assessed.
- Expected subjects with no signal, uncomputable values and empty boards are
reported as not assessed.
+22
View File
@@ -0,0 +1,22 @@
# Contributing
Issues and merge requests are welcome at
https://gitlab.com/extant2000/one-pane.
## Ground rules
- **A test that cannot fail proves nothing.** If you fix a bug, add a test
that fails without your fix, and check that it does fail before you submit.
- Measure, do not assume. Two modules with the same line count can be
different programs.
- Keep dependencies minimal. Every new dependency needs a reason.
- Explain why in comments, not what.
## Before you open a merge request
Run both of these and make sure they pass:
```
npm run build
npm test
```
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Extant 2000 LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+85
View File
@@ -0,0 +1,85 @@
# OnePane
Rolls health signals from different tools and stacks into one report, and
refuses to show a signal as healthy when it cannot be trusted.
## What it does and why
Collecting status from an uptime checker, an error tracker, CI and each
service's own endpoints into one board is mostly plumbing. The part that is
not plumbing is knowing when a green panel is lying. It lies in two specific
ways, and OnePane is built against both.
### Silence shown as health
An error board shows zero events for every product. They are not healthy:
the ingestion quota ran out and nothing is being recorded. Every panel is
green because no signal arrived, and that is the one state a dashboard must
never draw the same as "fine". So a reading older than the freshness window
(15 minutes by default) is reported as not assessed, with the note that the
panel is showing its last reading, not the current state. A reading with no
timestamp is treated the same way, because it cannot be told apart from a
stale one.
### Shared identity taken as per-subject evidence
Several products report through one error-tracking project. A per-product
search returns nothing and reads as "zero errors in this product", when it
means "this product cannot be separated in the data". Absence of attributed
events is not absence of events. When two subjects from the same source
share an `attributedTo` value, `ambiguousAttribution()` names them ("errors
cannot separate api, shop"), and every per-subject reading from that source
is reported as not assessed. A named list is something you can act on;
"attribution ambiguous" is not.
The rest of the rules:
- a subject in `expectedSubjects` with no signal at all is reported, since it
is green only because nothing was ever wired to it;
- a `null` value with no health state is not assessed, never coerced to zero;
- `down` fails at critical severity and `degraded` at high;
- an empty board is not assessed, never healthy.
The library is pure: no HTTP and no clock. `now` is a parameter, because a
freshness check that reads the clock cannot be tested and its output cannot
be reproduced. Reports use the `@extant2000/evidence-record` format.
## Usage
```ts
import { unify, formatPane, ambiguousAttribution, type Signal } from '@extant2000/one-pane'
const signals: Signal[] = [
{ source: 'uptime', subject: 'shop', metric: 'availability', value: 100, health: 'up',
observedAt: '2026-09-28T11:58:00Z', where: 'https://status.example.com' },
{ source: 'errors', subject: 'shop', metric: 'errors', value: 0, health: 'up',
observedAt: '2026-09-28T11:59:00Z', attributedTo: 'shared-project', where: 'error tracker search' },
{ source: 'errors', subject: 'api', metric: 'errors', value: 0, health: 'up',
observedAt: '2026-09-28T11:59:00Z', attributedTo: 'shared-project', where: 'error tracker search' },
{ source: 'ci', subject: 'billing', metric: 'last-deploy', value: 1, health: 'up',
observedAt: '2026-09-26T08:00:00Z', where: 'ci pipeline' },
]
const opts = {
now: '2026-09-28T12:00:00Z', // injected, never read from the clock
maxAgeSeconds: 900,
expectedSubjects: ['shop', 'api', 'billing', 'docs'],
}
const report = unify(signals, opts)
console.log(formatPane(signals, opts))
```
In this example only the shop availability reading passes. The two error
counts of zero are not assessed because both products share one project, the
billing reading is two days old, and `docs` has no signal at all.
## Install
```
npm install @extant2000/one-pane
```
## License
MIT. See [LICENSE](LICENSE).
+6
View File
@@ -0,0 +1,6 @@
# Security
Please report vulnerabilities privately by email to security@extant2000.com.
Do not open a public issue for a security problem.
We aim to acknowledge every report within 5 business days.
+1503
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
{
"name": "@extant2000/one-pane",
"version": "0.1.0",
"private": false,
"description": "Unifies health signals across stacks and refuses to show stale, unstamped or unattributable signals as healthy.",
"license": "MIT",
"type": "module",
"main": "./dist-cjs/index.js",
"files": [
"dist",
"dist-cjs",
"src",
"README.md",
"LICENSE"
],
"repository": {
"type": "git",
"url": "https://gitlab.com/extant2000/one-pane.git"
},
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist-cjs/index.js"
}
},
"scripts": {
"build": "tsc && tsc -p tsconfig.cjs.json && node -e \"require('fs').writeFileSync('dist-cjs/package.json', JSON.stringify({type:'commonjs'}))\"",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"devDependencies": {
"typescript": "^5.6.0",
"vitest": "^2.1.0"
},
"dependencies": {
"@extant2000/evidence-record": "^0.1.2"
},
"author": "Extant 2000 LLC",
"homepage": "https://gitlab.com/extant2000/one-pane",
"bugs": {
"url": "https://gitlab.com/extant2000/one-pane/-/issues"
}
}
BIN
View File
Binary file not shown.
+133
View File
@@ -0,0 +1,133 @@
import { describe, expect, it } from 'vitest'
import { overall, validateReport } from '@extant2000/evidence-record'
import { ambiguousAttribution, formatPane, unify, type Signal } from '../src/index.js'
const NOW = '2026-08-01T12:00:00Z'
const fresh = '2026-08-01T11:58:00Z'
const old = '2026-08-01T09:00:00Z'
const sig = (p: Partial<Signal> = {}): Signal => ({
source: 'gatus',
subject: 'crm',
metric: 'availability',
value: 100,
health: 'up',
observedAt: fresh,
where: 'https://status.example.com',
...p,
})
describe('silence is not health', () => {
it('a stale panel is NOT ASSESSED, not green', () => {
// An error board can show zero events because ingestion stopped, not
// because nothing went wrong.
const r = unify([sig({ observedAt: old })], { now: NOW })
expect(r.findings[0]!.determination).toBe('not-assessed')
expect(overall(r)).toBe('not-assessed')
const text = formatPane([sig({ observedAt: old })], { now: NOW })
expect(text).toContain('Silence is not health')
expect(text).toContain('showing its last reading')
expect(text).not.toContain('✓')
})
it('passes a reading inside the freshness window', () => {
expect(overall(unify([sig()], { now: NOW }))).toBe('pass')
})
it('honours a custom freshness window', () => {
// 2 minutes old, with a 60-second window.
expect(overall(unify([sig()], { now: NOW, maxAgeSeconds: 60 }))).toBe('not-assessed')
})
it('treats an unstamped reading as indistinguishable from a stale one', () => {
const r = unify([sig({ observedAt: undefined })], { now: NOW })
expect(r.findings[0]!.determination).toBe('not-assessed')
expect(r.findings[0]!.detail).toContain('may be current; nothing here establishes that')
})
it('reports an expected subject with no signal at all', () => {
const r = unify([sig({ subject: 'crm' })], { now: NOW, expectedSubjects: ['crm', 'hr', 'desk'] })
const missing = r.findings.filter(f => f.id.endsWith('/missing'))
expect(missing).toHaveLength(2)
expect(missing[0]!.detail).toContain('green because nothing was ever wired to it')
})
})
describe('shared identity is not per-subject evidence', () => {
// Several products reporting through ONE error-tracking project. A
// per-product search returning nothing means "not separable", not "no errors".
const shared = [
sig({ source: 'sentry', subject: 'crm', metric: 'errors', value: 0, attributedTo: 'shared-project' }),
sig({ source: 'sentry', subject: 'hr', metric: 'errors', value: 0, attributedTo: 'shared-project' }),
sig({ source: 'sentry', subject: 'desk', metric: 'errors', value: 0, attributedTo: 'shared-project' }),
]
it('names the entangled subjects rather than saying "ambiguous"', () => {
const groups = ambiguousAttribution(shared)
expect(groups).toHaveLength(1)
expect(groups[0]!.subjects).toEqual(['crm', 'desk', 'hr'])
})
it('refuses to read a shared-identity zero as a per-subject pass', () => {
const r = unify(shared, { now: NOW })
expect(overall(r)).toBe('not-assessed')
for (const f of r.findings.filter(f => f.id.startsWith('crm/'))) {
expect(f.determination).toBe('not-assessed')
}
})
it('says what a search returning nothing actually means', () => {
const text = formatPane(shared, { now: NOW })
expect(text).toContain('absence of attributed events is not absence of events')
expect(text).toContain('crm, desk, hr')
})
it('leaves a uniquely-attributed signal alone', () => {
const distinct = [
sig({ source: 'sentry', subject: 'crm', attributedTo: 'crm-prod' }),
sig({ source: 'sentry', subject: 'hr', attributedTo: 'hr-prod' }),
]
expect(ambiguousAttribution(distinct)).toEqual([])
expect(overall(unify(distinct, { now: NOW }))).toBe('pass')
})
it('does not entangle the same identity across different sources', () => {
// gatus/"prod" and sentry/"prod" are unrelated namespaces.
const r = ambiguousAttribution([
sig({ source: 'gatus', subject: 'crm', attributedTo: 'prod' }),
sig({ source: 'sentry', subject: 'hr', attributedTo: 'prod' }),
])
expect(r).toEqual([])
})
})
describe('health states', () => {
it('reports down as critical and degraded as high', () => {
expect(unify([sig({ health: 'down' })], { now: NOW }).findings[0]!.severity).toBe('critical')
expect(unify([sig({ health: 'degraded' })], { now: NOW }).findings[0]!.severity).toBe('high')
expect(overall(unify([sig({ health: 'down' })], { now: NOW }))).toBe('fail')
})
it('reports an uncomputable value as not-assessed, not zero', () => {
const r = unify([sig({ value: null, health: undefined })], { now: NOW })
expect(r.findings[0]!.determination).toBe('not-assessed')
})
it('carries the sample count into the citation for a null value', () => {
const text = formatPane([sig({ value: null, health: undefined })], { now: NOW, evidence: 'inline' })
expect(text).toContain('not computable')
expect(text).toContain('0 samples')
})
})
describe('conformance with the evidence-record standard', () => {
it('an empty board is not-assessed, never healthy', () => {
const r = unify([], { now: NOW })
expect(overall(r)).toBe('not-assessed')
expect(formatPane([], { now: NOW })).toContain('An empty board is not a healthy one')
})
it('every conclusion carries a citation', () => {
expect(validateReport(unify([sig(), sig({ subject: 'hr', health: 'down' })], { now: NOW }))).toEqual([])
})
})
+9
View File
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node",
"outDir": "dist-cjs",
"declaration": false
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "ES2022", "module": "ES2022", "moduleResolution": "bundler",
"declaration": true, "outDir": "dist", "rootDir": "src",
"strict": true, "skipLibCheck": true
},
"include": ["src/**/*.ts"]
}