@kumbatio/energy-system 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/README.md +64 -5
- package/api-surface.json +2 -1
- package/conformance.json +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +18 -11
- package/dist/engine.js.map +1 -1
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +94 -30
- package/dist/gate.js.map +1 -1
- package/dist/levels.d.ts +16 -0
- package/dist/levels.d.ts.map +1 -1
- package/dist/levels.js +54 -1
- package/dist/levels.js.map +1 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +16 -23
- package/dist/persistence.js.map +1 -1
- package/package.json +16 -11
- package/spec/conformance.schema.json +395 -0
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,67 @@ change to [SPEC.md](./SPEC.md), not only to this library. The same is true of th
|
|
|
12
12
|
reconciliation rule, which two implementations must agree on to share state at
|
|
13
13
|
all. Prose returned by `describe()` is not covered; wording is a product decision.
|
|
14
14
|
|
|
15
|
+
## [Unreleased]
|
|
16
|
+
|
|
17
|
+
Corrections to `1.0.0`. No type signature changed, but two of these change
|
|
18
|
+
runtime behavior and one narrows an install range — see the note at the top of
|
|
19
|
+
this file on why that is more than a patch for this package.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **The notification gate re-judges everything it is holding when energy or
|
|
24
|
+
suppression changes.** A notification was classified once, when published,
|
|
25
|
+
and an open batch window was then delivered under whatever policy happened to
|
|
26
|
+
be in force later. So an intent admitted at Steady arrived in the middle of a
|
|
27
|
+
focus session, and one batched at Steady was surfaced at Rest with every
|
|
28
|
+
channel disabled. `flush()` bypassed active suppression the same way.
|
|
29
|
+
Batched intents the current policy no longer admits are now moved to the
|
|
30
|
+
deferred queue instead, and released when something admits them. The batch
|
|
31
|
+
deadline is anchored to when the window opened, so a config change moves the
|
|
32
|
+
deadline rather than restarting the wait.
|
|
33
|
+
- **External state is validated against the published JSON Schema exactly.**
|
|
34
|
+
`createEnergyState()` accepted fractional timestamps, which
|
|
35
|
+
[spec/energy-state.schema.json](./spec/energy-state.schema.json) does not
|
|
36
|
+
allow, and persisted state carrying unknown properties was silently trimmed
|
|
37
|
+
to fit rather than rejected — so two implementations could exchange a state
|
|
38
|
+
and disagree about what they had exchanged. Persistence loads, cross-context
|
|
39
|
+
observations and `memoryPersistence` now share one strict boundary parser.
|
|
40
|
+
- **A configured `originId` no longer corrupts the unproduced sentinel.**
|
|
41
|
+
Construction stamped the configured producer identity onto the untouched
|
|
42
|
+
default state, which SPEC.md §3.2 requires to stay distinguishable from a
|
|
43
|
+
real one. `isUnproducedState()` returned `false` for it and
|
|
44
|
+
`getEnergyMetrics()` reported an age measured from the epoch. The sentinel is
|
|
45
|
+
now always `origin: "0-initial"`; the configured identity owns the first
|
|
46
|
+
state the engine actually produces.
|
|
47
|
+
- **`api-surface.json` includes `EnergyEngine.resolve()`.** The declaration
|
|
48
|
+
parser did not recognise generic members, so a public method was missing from
|
|
49
|
+
the frozen surface — and a method absent from the freeze is a method nobody
|
|
50
|
+
notices removing.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **React peer range is now `>=19.2.0`** for both `react` and `@types/react`.
|
|
55
|
+
The React entry point imports `<Activity>`, added in React 19.2, so the
|
|
56
|
+
previous `>=19` advertised a compatibility that throws on first render under
|
|
57
|
+
19.0 and 19.1.
|
|
58
|
+
- **Generated artifacts are checked, not regenerated, during validation.**
|
|
59
|
+
`pnpm test` used to run the full build first, so the drift guard compared
|
|
60
|
+
`conformance.json` against a copy it had just written — it could not fail,
|
|
61
|
+
whatever was committed. Generation now belongs to `pnpm run build`; both
|
|
62
|
+
generators take `--check`, and both artifacts are verified by the suite.
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- **[spec/conformance.schema.json](./spec/conformance.schema.json)** — the
|
|
67
|
+
schema `conformance.json` has always pointed at via `$schema` and which did
|
|
68
|
+
not exist. The generator now validates its own output against it before
|
|
69
|
+
emitting, and it is exported from the package so the relative reference
|
|
70
|
+
resolves for consumers.
|
|
71
|
+
- Packaged-consumer tests: the suite packs a tarball, unpacks it, and asserts
|
|
72
|
+
that every `exports` target is present, that all four entry points import,
|
|
73
|
+
that the conformance `$schema` reference resolves from the package root, and
|
|
74
|
+
that the React peer floor matches the APIs the entry point imports.
|
|
75
|
+
|
|
15
76
|
## [1.0.0]
|
|
16
77
|
|
|
17
78
|
The API is frozen. Everything below documents what that commitment now covers.
|
package/README.md
CHANGED
|
@@ -5,6 +5,48 @@ Framework-agnostic TypeScript library for building **energy-aware applications**
|
|
|
5
5
|
Instead of adapting software to clock time, adapt behavior to current cognitive capacity.
|
|
6
6
|
`energy-system` models energy as explicit state and resolves strategies from that state.
|
|
7
7
|
|
|
8
|
+
## At a glance
|
|
9
|
+
|
|
10
|
+
A library about low capacity should be readable at low capacity. Everything you
|
|
11
|
+
need to use it is here; the rest of this file is reference.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @kumbatio/energy-system
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createEnergyEngine, uiVisibilityStrategy } from '@kumbatio/energy-system'
|
|
19
|
+
|
|
20
|
+
const engine = createEnergyEngine({ initialLevel: 75 })
|
|
21
|
+
|
|
22
|
+
engine.setLevel(25) // the person says they are running low
|
|
23
|
+
engine.resolve(uiVisibilityStrategy) // -> what the UI should do about it
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
React:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { EnergyProvider, useEnergy } from '@kumbatio/energy-system/react'
|
|
30
|
+
|
|
31
|
+
const { state, setLevel } = useEnergy()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| If you want to | Go to |
|
|
35
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
36
|
+
| Set and read energy | [Quick start (core)](#quick-start-core) |
|
|
37
|
+
| Use it in React | [Quick start (React)](#quick-start-react) |
|
|
38
|
+
| Style by level in CSS | [CSS usage](#css-usage) |
|
|
39
|
+
| Show or hide by level | [Presence annotation](#presence-annotation-which-energy-states-does-this-element-belong-to) |
|
|
40
|
+
| Time-box work, hold notifications | [Focus sessions and the notification gate](#focus-sessions-and-the-notification-gate) |
|
|
41
|
+
| Offer "not now" | [Deferral](#deferral-not-now) |
|
|
42
|
+
| Decide what may interrupt | [Inbound demand and autonomy](#inbound-demand-and-autonomy) |
|
|
43
|
+
| Keep state across reloads and tabs | [Persistence and reconciliation](#persistence-and-reconciliation) |
|
|
44
|
+
| Look up an export | [API map](#api-map) |
|
|
45
|
+
| Port this to another language | [Specification and conformance](#specification-and-conformance) |
|
|
46
|
+
|
|
47
|
+
Five levels, and they never change: `100` Peak, `75` Active, `50` Steady,
|
|
48
|
+
`25` Low, `0` Rest.
|
|
49
|
+
|
|
8
50
|
## Why this exists
|
|
9
51
|
|
|
10
52
|
Most tooling assumes equal capacity across a day. Real-world cognitive energy is variable and non-linear.
|
|
@@ -533,15 +575,21 @@ The model is specified independently of this implementation.
|
|
|
533
575
|
two processes (or two languages) can share one person's energy state.
|
|
534
576
|
- **[conformance.json](./conformance.json)** — every table and every decision above, as vectors.
|
|
535
577
|
Ships in the package.
|
|
578
|
+
- **[spec/conformance.schema.json](./spec/conformance.schema.json)** — the shape of that vector
|
|
579
|
+
file, so a port can tell a file it can trust from one whose structure moved under it. Ships too,
|
|
580
|
+
and the generator validates its own output against it.
|
|
536
581
|
|
|
537
582
|
```ts
|
|
538
583
|
import conformance from '@kumbatio/energy-system/conformance.json' with { type: 'json' }
|
|
539
584
|
```
|
|
540
585
|
|
|
541
586
|
An implementation in another language passes by loading the vectors and replaying them; this
|
|
542
|
-
package's own `test/conformance.test.ts` does exactly that and is a reasonable model to copy.
|
|
543
|
-
|
|
544
|
-
|
|
587
|
+
package's own `test/conformance.test.ts` does exactly that and is a reasonable model to copy.
|
|
588
|
+
|
|
589
|
+
The file is generated from the built library, so the vectors cannot drift from the behavior they
|
|
590
|
+
describe. `pnpm run build` regenerates it; `pnpm test` recomputes it and fails if what is committed
|
|
591
|
+
differs, without rewriting anything. The two are deliberately separate — a check that regenerates
|
|
592
|
+
first is comparing a file to itself.
|
|
545
593
|
|
|
546
594
|
Vectors cover the pure surface: tables, and functions of their arguments alone. The stateful
|
|
547
595
|
guarantees — defer-never-drop, session auto-expiry, persistence ordering — are normative in SPEC.md
|
|
@@ -563,14 +611,25 @@ means more than the type signatures:
|
|
|
563
611
|
|
|
564
612
|
## Development
|
|
565
613
|
|
|
614
|
+
```bash
|
|
615
|
+
pnpm run validate # format, lint, types, tests, packaging — what CI runs
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
Individually:
|
|
619
|
+
|
|
566
620
|
```bash
|
|
567
621
|
pnpm run check-types
|
|
568
622
|
pnpm run lint
|
|
569
|
-
pnpm test
|
|
570
|
-
pnpm run build
|
|
623
|
+
pnpm test # compiles, then CHECKS the generated artifacts
|
|
624
|
+
pnpm run build # compiles, then REGENERATES them
|
|
571
625
|
pnpm run pack:dry-run
|
|
572
626
|
```
|
|
573
627
|
|
|
628
|
+
`build` is the only thing that writes `api-surface.json` and `conformance.json`.
|
|
629
|
+
If a change to the library moves either, `pnpm test` fails and tells you to run
|
|
630
|
+
`pnpm run build` and commit the result — that is the intended loop, not a
|
|
631
|
+
warning to work around.
|
|
632
|
+
|
|
574
633
|
## Notes
|
|
575
634
|
|
|
576
635
|
This package is framework-agnostic at its core. Platform-specific persistence
|
package/api-surface.json
CHANGED
package/conformance.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./spec/conformance.schema.json",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"timezone": "UTC",
|
|
5
5
|
"note": "Generated from the built library by scripts/extract-conformance.ts. See SPEC.md for the normative rules these vectors encode, including the runtime invariants that no vector can express.",
|
|
6
6
|
"levels": [
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACZ,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACjE,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,KAAK,IAAI,IAAI,CAAA;IACb,+BAA+B;IAC/B,QAAQ,IAAI,WAAW,CAAA;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IACzD,iCAAiC;IACjC,UAAU,IAAI,IAAI,CAAA;IAClB,gEAAgE;IAChE,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAAA;IACrD,sDAAsD;IACtD,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IAC9C,8EAA8E;IAC9E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,mDAAmD;IACnD,OAAO,IAAI,IAAI,CAAA;CAChB;AAgDD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAgXlF"}
|
package/dist/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UNPRODUCED_ORIGIN, UNPRODUCED_TIMESTAMP, createEnergyOrigin, createEnergyState, cycleEnergyLevel,
|
|
1
|
+
import { UNPRODUCED_ORIGIN, UNPRODUCED_TIMESTAMP, createEnergyOrigin, createEnergyState, cycleEnergyLevel, parseExternalEnergyState, } from './levels.js';
|
|
2
2
|
import { isPreferredEnergyState } from './reconcile.js';
|
|
3
3
|
function logEngineError(message, err) {
|
|
4
4
|
console.error(`[energy-system] ${message}`, err);
|
|
@@ -20,17 +20,18 @@ function isSameState(a, b) {
|
|
|
20
20
|
a.revision === b.revision &&
|
|
21
21
|
a.origin === b.origin);
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate state that arrived from outside this engine. `candidate` is typed
|
|
25
|
+
* as `EnergyState`, but it crossed a process, tab or storage boundary to get
|
|
26
|
+
* here, so nothing about its runtime shape is guaranteed — it is parsed
|
|
27
|
+
* against the published schema before anything else looks at it.
|
|
28
|
+
*/
|
|
23
29
|
function normalizeState(candidate, nowMs, maxFutureSkewMs) {
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
const parsed = parseExternalEnergyState(candidate);
|
|
31
|
+
if (parsed.timestamp - nowMs > maxFutureSkewMs) {
|
|
32
|
+
throw new Error(`Energy state timestamp ${String(parsed.timestamp)} exceeds local clock by more than ${String(maxFutureSkewMs)}ms`);
|
|
26
33
|
}
|
|
27
|
-
|
|
28
|
-
throw new Error(`Invalid energy source from persistence: ${String(candidate.source)}`);
|
|
29
|
-
}
|
|
30
|
-
if (candidate.timestamp - nowMs > maxFutureSkewMs) {
|
|
31
|
-
throw new Error(`Energy state timestamp ${String(candidate.timestamp)} exceeds local clock by more than ${String(maxFutureSkewMs)}ms`);
|
|
32
|
-
}
|
|
33
|
-
return createEnergyState(candidate.level, candidate.source, candidate.timestamp, candidate.revision, candidate.origin);
|
|
34
|
+
return parsed;
|
|
34
35
|
}
|
|
35
36
|
export function createEnergyEngine(options = {}) {
|
|
36
37
|
const { initialLevel = 100, persistence, onChange, onPersistenceError, clock, maxFutureSkewMs = DEFAULT_MAX_FUTURE_SKEW_MS, autoStart = true, } = options;
|
|
@@ -59,8 +60,14 @@ export function createEnergyEngine(options = {}) {
|
|
|
59
60
|
* React render (which is what every provider does) stays prerender-safe. Both sentinels sort
|
|
60
61
|
* below any real value, so the first persisted, observed or user-set state replaces this
|
|
61
62
|
* unconditionally.
|
|
63
|
+
*
|
|
64
|
+
* The origin is ALWAYS the sentinel, never `options.originId`. SPEC.md §3.2 requires the
|
|
65
|
+
* untouched default to stay distinguishable from a real state, and `isUnproducedState()` tests
|
|
66
|
+
* exactly this pair — stamping a configured producer identity here turns "nobody has chosen
|
|
67
|
+
* yet" into a state that looks chosen, which reads downstream as a level set at the epoch.
|
|
68
|
+
* The configured identity belongs to the first state this engine actually produces.
|
|
62
69
|
*/
|
|
63
|
-
let state = createEnergyState(initialLevel, 'manual', UNPRODUCED_TIMESTAMP, 0,
|
|
70
|
+
let state = createEnergyState(initialLevel, 'manual', UNPRODUCED_TIMESTAMP, 0, UNPRODUCED_ORIGIN);
|
|
64
71
|
// Version 0 is the initial in-memory state, not proof that a persistence
|
|
65
72
|
// adapter has durably stored it. Starting below the version domain keeps
|
|
66
73
|
// flush() honest even before the first state transition.
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAwEvD,SAAS,cAAc,CAAC,OAAe,EAAE,GAAY;IACnD,OAAO,CAAC,KAAK,CAAC,mBAAmB,OAAO,EAAE,EAAE,GAAG,CAAC,CAAA;AAClD,CAAC;AAED,MAAM,wBAAwB,GAAG,GAAG,CAAA;AACpC,MAAM,oBAAoB,GAAG,MAAM,CAAA;AACnC,MAAM,0BAA0B,GAAG,CAAC,GAAG,MAAM,CAAA;AAE7C,SAAS,UAAU,CAAC,KAAoC;IACtD,IAAI,OAAO,KAAK,KAAK,UAAU;QAAE,OAAO,KAAK,CAAA;IAC7C,IAAI,KAAK,EAAE,GAAG;QAAE,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;IACxC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAA;AACzB,CAAC;AAED,SAAS,WAAW,CAAC,CAAc,EAAE,CAAc;IACjD,OAAO,CACL,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;QACnB,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;QAC3B,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QACrB,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;QACzB,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CACtB,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,cAAc,CACrB,SAAsB,EACtB,KAAa,EACb,eAAuB;IAEvB,MAAM,MAAM,GAAG,wBAAwB,CAAC,SAAS,CAAC,CAAA;IAElD,IAAI,MAAM,CAAC,SAAS,GAAG,KAAK,GAAG,eAAe,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,0BAA0B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,qCAAqC,MAAM,CAAC,eAAe,CAAC,IAAI,CACnH,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAO,GAAwB,EAAE;IAClE,MAAM,EACJ,YAAY,GAAG,GAAG,EAClB,WAAW,EACX,QAAQ,EACR,kBAAkB,EAClB,KAAK,EACL,eAAe,GAAG,0BAA0B,EAC5C,SAAS,GAAG,IAAI,GACjB,GAAG,OAAO,CAAA;IAEX;;;OAGG;IACH,IAAI,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAA;IACvC,MAAM,cAAc,GAAG,GAAW,EAAE;QAClC,gBAAgB,KAAK,kBAAkB,EAAE,CAAA;QACzC,OAAO,gBAAgB,CAAA;IACzB,CAAC,CAAA;IAED,IACE,OAAO,eAAe,KAAK,QAAQ;QACnC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,eAAe,KAAK,MAAM,CAAC,iBAAiB,CAAC;QACnF,eAAe,GAAG,CAAC,EACnB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,4BAA4B,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAA;IACxE,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwB,CAAA;IACjD,MAAM,iBAAiB,GAAoD,EAAE,CAAA;IAC7E,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,IAAI,WAAW,GAAG,KAAK,CAAA;IAEvB;;;;;;;;;;;OAWG;IACH,IAAI,KAAK,GAAgB,iBAAiB,CACxC,YAAY,EACZ,QAAQ,EACR,oBAAoB,EACpB,CAAC,EACD,iBAAiB,CAClB,CAAA;IACD,yEAAyE;IACzE,yEAAyE;IACzE,yDAAyD;IACzD,IAAI,gBAAgB,GAAG,CAAC,CAAC,CAAA;IACzB,IAAI,uBAAuB,GAAG,CAAC,CAAA;IAC/B,IAAI,YAAY,GAAG,KAAK,CAAA;IACxB,IAAI,iBAA4D,CAAA;IAChE,IAAI,mBAAmB,GAAG,wBAAwB,CAAA;IAClD,IAAI,oBAA+C,CAAA;IACnD,IAAI,2BAA2B,GAAG,KAAK,CAAA;IACvC,IAAI,oBAA6B,CAAA;IACjC,MAAM,cAAc,GAIf,EAAE,CAAA;IAEP,SAAS,qBAAqB;QAC5B,KAAK,IAAI,KAAK,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACnE,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;YACpC,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC;gBACjD,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;gBAC/B,MAAM,CAAC,OAAO,EAAE,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,UAAU,iBAAiB,CAAC,KAAwB;QACvD,OAAO,CAAC,QAAQ,IAAI,gBAAgB,GAAG,uBAAuB,EAAE,CAAC;YAC/D,MAAM,QAAQ,GAAG,KAAK,CAAA;YACtB,MAAM,eAAe,GAAG,YAAY,CAAA;YAEpC,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBAC1B,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAA;gBAC9D,mBAAmB,GAAG,wBAAwB,CAAA;gBAC9C,qBAAqB,EAAE,CAAA;YACzB,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,cAAc,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAA;gBACrD,IAAI,kBAAkB,EAAE,CAAC;oBACvB,IAAI,CAAC;wBACH,kBAAkB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;oBACnC,CAAC;oBAAC,OAAO,GAAY,EAAE,CAAC;wBACtB,cAAc,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAA;oBAC1D,CAAC;gBACH,CAAC;gBACD,YAAY,GAAG,KAAK,CAAA;gBAEpB,IAAI,CAAC,QAAQ,IAAI,CAAC,iBAAiB,IAAI,gBAAgB,GAAG,uBAAuB,EAAE,CAAC;oBAClF,MAAM,YAAY,GAAG,mBAAmB,CAAA;oBACxC,kEAAkE;oBAClE,iDAAiD;oBACjD,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,GAAG,CAAC,EAAE,oBAAoB,CAAC,CAAA;oBAC7E,iBAAiB,GAAG,UAAU,CAAC,GAAG,EAAE;wBAClC,iBAAiB,GAAG,SAAS,CAAA;wBAC7B,YAAY,EAAE,CAAA;oBAChB,CAAC,EAAE,YAAY,CAAC,CAAA;gBAClB,CAAC;gBAED,OAAM;YACR,CAAC;QACH,CAAC;QAED,YAAY,GAAG,KAAK,CAAA;QAEpB,IAAI,CAAC,QAAQ,IAAI,gBAAgB,GAAG,uBAAuB,EAAE,CAAC;YAC5D,YAAY,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IAED,SAAS,YAAY;QACnB,IAAI,CAAC,WAAW,IAAI,QAAQ;YAAE,OAAM;QAEpC,uBAAuB,GAAG,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,YAAY,CAAC,CAAA;QAEzE,IAAI,YAAY;YAAE,OAAM;QAExB,YAAY,GAAG,IAAI,CAAA;QACnB,KAAK,iBAAiB,CAAC,WAAW,CAAC,CAAA;IACrC,CAAC;IAED,SAAS,MAAM,CAAC,IAAiB,EAAE,IAAiB;QAClD,iBAAiB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACtC,IAAI,WAAW;YAAE,OAAM;QAEvB,WAAW,GAAG,IAAI,CAAA;QAClB,IAAI,CAAC;YACH,OAAO,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,EAAE,CAAA;gBAC5C,IAAI,CAAC,UAAU;oBAAE,SAAQ;gBAEzB,IAAI,QAAQ,EAAE,CAAC;oBACb,IAAI,CAAC;wBACH,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,CAAA;oBAC5C,CAAC;oBAAC,OAAO,GAAY,EAAE,CAAC;wBACtB,cAAc,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAA;oBAChD,CAAC;gBACH,CAAC;gBAED,MAAM,mBAAmB,GAAG,CAAC,GAAG,SAAS,CAAC,CAAA;gBAC1C,KAAK,MAAM,QAAQ,IAAI,mBAAmB,EAAE,CAAC;oBAC3C,IAAI,CAAC;wBACH,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,CAAA;oBAC5C,CAAC;oBAAC,OAAO,GAAY,EAAE,CAAC;wBACtB,cAAc,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAA;oBAChD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,WAAW,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC;IAED,SAAS,UAAU,CAAC,IAAiB;QACnC,yEAAyE;QACzE,6DAA6D;QAC7D,IAAI,QAAQ,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC;YAAE,OAAO,KAAK,CAAA;QAEtD,MAAM,IAAI,GAAG,KAAK,CAAA;QAClB,KAAK,GAAG,IAAI,CAAA;QACZ,YAAY,IAAI,CAAC,CAAA;QACjB,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAClB,IAAI,WAAW,EAAE,CAAC;YAChB,YAAY,EAAE,CAAA;QAChB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,IAAI,6BAA6B,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;IAC5C,IAAI,OAAO,GAAG,KAAK,CAAA;IAEnB,MAAM,MAAM,GAAiB;QAC3B,KAAK;YACH,IAAI,OAAO,IAAI,QAAQ,IAAI,CAAC,WAAW;gBAAE,OAAM;YAC/C,OAAO,GAAG,IAAI,CAAA;YAEd,oBAAoB,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBAC7D,cAAc,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAA;YACnD,CAAC,CAAC,CAAA;YAEF,IAAI,CAAC,WAAW,CAAC,OAAO;gBAAE,OAAM;YAEhC,IAAI,CAAC;gBACH,6BAA6B,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,EAAE;oBACpE,IAAI,QAAQ;wBAAE,OAAM;oBAEpB,IAAI,UAAuB,CAAA;oBAE3B,IAAI,CAAC;wBACH,UAAU,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,eAAe,CAAC,CAAA;oBACpE,CAAC;oBAAC,OAAO,GAAY,EAAE,CAAC;wBACtB,cAAc,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAA;wBAC7D,OAAM;oBACR,CAAC;oBAED,IAAI,CAAC,sBAAsB,CAAC,UAAU,EAAE,KAAK,CAAC;wBAAE,OAAM;oBAEtD,UAAU,CAAC,UAAU,CAAC,CAAA;gBACxB,CAAC,CAAC,CAAA;YACJ,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,cAAc,CAAC,gDAAgD,EAAE,GAAG,CAAC,CAAA;YACvE,CAAC;QACH,CAAC;QAED,QAAQ;YACN,OAAO,KAAK,CAAA;QACd,CAAC;QAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ;YAC/B,IAAI,QAAQ;gBAAE,OAAM;YAEpB,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAA;YACtB,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;YACnD,IAAI,QAAQ,GAAG,CAAC,CAAA;YAEhB,IAAI,SAAS,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;gBAClC,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,gBAAgB,EAAE,CAAC;oBAC/C,8DAA8D;oBAC9D,qEAAqE;oBACrE,SAAS,IAAI,CAAC,CAAA;gBAChB,CAAC;qBAAM,CAAC;oBACN,QAAQ,GAAG,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAA;gBAC/B,CAAC;YACH,CAAC;YAED,UAAU,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC;QAED,UAAU;YACR,IAAI,QAAQ;gBAAE,OAAM;YACpB,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,CAAA;QAC1D,CAAC;QAED,SAAS,CAAC,QAAQ;YAChB,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;YACjB,CAAC;YAED,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACvB,OAAO,GAAG,EAAE;gBACV,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YAC5B,CAAC,CAAA;QACH,CAAC;QAED,OAAO,CAAI,QAA+B;YACxC,OAAO,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACtC,CAAC;QAED,KAAK,CAAC,OAAO;YACX,IAAI,CAAC,WAAW,IAAI,QAAQ;gBAAE,OAAM;YAEpC,MAAM,mBAAmB,GAAG,YAAY,CAAA;YACxC,IAAI,MAA0B,CAAA;YAE9B,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAA;gBACjC,2BAA2B,GAAG,IAAI,CAAA;gBAClC,oBAAoB,GAAG,SAAS,CAAA;YAClC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,oBAAoB,GAAG,GAAG,CAAA;gBAC1B,cAAc,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAA;gBAC5D,OAAM;YACR,CAAC;YAED,IAAI,CAAC,MAAM,IAAI,QAAQ;gBAAE,OAAM;YAE/B,IAAI,UAAuB,CAAA;YAE3B,IAAI,CAAC;gBACH,UAAU,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,eAAe,CAAC,CAAA;YAC7D,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,cAAc,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAA;gBAC9D,OAAM;YACR,CAAC;YAED,IAAI,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC;gBAAE,OAAM;YAE1C,IAAI,mBAAmB,KAAK,YAAY,IAAI,sBAAsB,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;gBACtF,UAAU,CAAC,UAAU,CAAC,CAAA;YACxB,CAAC;QACH,CAAC;QAED,KAAK,CAAC,KAAK;YACT,IAAI,CAAC,WAAW;gBAAE,OAAM;YACxB,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;YAC1D,CAAC;YAED,uEAAuE;YACvE,kEAAkE;YAClE,wEAAwE;YACxE,IAAI,YAAY,KAAK,CAAC,IAAI,oBAAoB,EAAE,CAAC;gBAC/C,MAAM,oBAAoB,CAAA;YAC5B,CAAC;YAED,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;YAC1D,CAAC;YAED,IAAI,YAAY,KAAK,CAAC,IAAI,CAAC,2BAA2B,EAAE,CAAC;gBACvD,MAAM,IAAI,KAAK,CACb,sFAAsF,EACtF,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAChC,CAAA;YACH,CAAC;YAED,MAAM,aAAa,GAAG,YAAY,CAAA;YAClC,IAAI,gBAAgB,IAAI,aAAa;gBAAE,OAAM;YAE7C,uBAAuB,GAAG,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,aAAa,CAAC,CAAA;YAC1E,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACpD,cAAc,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAA;YAClE,CAAC,CAAC,CAAA;YACF,YAAY,EAAE,CAAA;YACd,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAM;YAEpB,QAAQ,GAAG,IAAI,CAAA;YACf,IAAI,CAAC;gBACH,6BAA6B,EAAE,CAAA;YACjC,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACtB,cAAc,CAAC,2CAA2C,EAAE,GAAG,CAAC,CAAA;YAClE,CAAC;YACD,IAAI,iBAAiB,EAAE,CAAC;gBACtB,YAAY,CAAC,iBAAiB,CAAC,CAAA;gBAC/B,iBAAiB,GAAG,SAAS,CAAA;YAC/B,CAAC;YACD,SAAS,CAAC,KAAK,EAAE,CAAA;YACjB,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAA;YAC5B,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;YACrF,KAAK,MAAM,MAAM,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;YAC7B,CAAC;QACH,CAAC;KACF,CAAA;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,EAAE,CAAA;IAChB,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
|
package/dist/gate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AA2BjE,+CAA+C;AAC/C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAQjE,qEAAqE;AACrE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAEpF;AAED,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAED,kCAAkC;AAClC,MAAM,MAAM,0BAA0B,GAAG,WAAW,GAAG,OAAO,GAAG,UAAU,CAAA;AAE3E,sEAAsE;AACtE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACrD,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAA;IAC3C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAC5B;AAED,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAA;AAEjE;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,oBAAoB,EAC9B,UAAU,EAAE,OAAO,GAClB,cAAc,CAiBhB;AAED,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;CACpC;AAWD,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAA;IAC/C,uFAAuF;IACvF,QAAQ,CAAC,EAAE,kBAAkB,CAAC,kBAAkB,CAAC,CAAA;IACjD,sDAAsD;IACtD,KAAK,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAA;IACpC,uDAAuD;IACvD,SAAS,CAAC,EAAE,aAAa,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,OAAO,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,oBAAoB,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,CAAA;IACtF;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAAA;IACxC,YAAY,IAAI,OAAO,CAAA;IACvB,qEAAqE;IACrE,YAAY,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD,6EAA6E;IAC7E,KAAK,IAAI,IAAI,CAAA;IACb;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAA;CAChB;AAQD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,uBAAuB,GAC/B,gBAAgB,CA8OlB"}
|
package/dist/gate.js
CHANGED
|
@@ -9,6 +9,11 @@ import { notificationStrategy } from './strategies.js';
|
|
|
9
9
|
* reminders that came due while suppressed): the gate NEVER silently drops a
|
|
10
10
|
* notification. Anything not deliverable now is deferred and released when
|
|
11
11
|
* energy rises, suppression lifts, or the gate is disposed.
|
|
12
|
+
*
|
|
13
|
+
* Its mirror image matters just as much: a notification classified when it was
|
|
14
|
+
* published must not be delivered under a policy that would now refuse it.
|
|
15
|
+
* Energy and suppression both move inside an open batch window, so every held
|
|
16
|
+
* intent is re-classified whenever either changes.
|
|
12
17
|
*/
|
|
13
18
|
function logGateError(message, err) {
|
|
14
19
|
console.error(`[energy-system] ${message}`, err);
|
|
@@ -80,6 +85,15 @@ export function createNotificationGate(engine, options) {
|
|
|
80
85
|
let batch = [];
|
|
81
86
|
let deferred = [];
|
|
82
87
|
let batchTimer;
|
|
88
|
+
let batchTimerIntervalMs;
|
|
89
|
+
/*
|
|
90
|
+
* When the currently open batch window started. The window is anchored here
|
|
91
|
+
* rather than to the timer, so a config change that lengthens `batchInterval`
|
|
92
|
+
* moves the deadline relative to the *opening* of the batch instead of
|
|
93
|
+
* restarting the wait. Energy oscillating across a threshold would otherwise
|
|
94
|
+
* re-arm the timer forever and starve the batch.
|
|
95
|
+
*/
|
|
96
|
+
let batchOpenedAt;
|
|
83
97
|
function currentConfig() {
|
|
84
98
|
return strategy.resolve(engine.getState().level);
|
|
85
99
|
}
|
|
@@ -112,47 +126,94 @@ export function createNotificationGate(engine, options) {
|
|
|
112
126
|
scheduler.clearTimeout(batchTimer);
|
|
113
127
|
batchTimer = undefined;
|
|
114
128
|
}
|
|
129
|
+
batchTimerIntervalMs = undefined;
|
|
115
130
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
function scheduleBatch(intervalMs) {
|
|
125
|
-
if (batchTimer !== undefined)
|
|
131
|
+
/**
|
|
132
|
+
* Arm (or re-arm) the batch timer for the interval the active config asks
|
|
133
|
+
* for. A window already running at that interval is left alone so its
|
|
134
|
+
* elapsed time survives unrelated transitions.
|
|
135
|
+
*/
|
|
136
|
+
function armBatchTimer(intervalMs) {
|
|
137
|
+
if (batchTimer !== undefined && batchTimerIntervalMs === intervalMs)
|
|
126
138
|
return;
|
|
139
|
+
clearBatchTimer();
|
|
140
|
+
batchTimerIntervalMs = intervalMs;
|
|
141
|
+
const dueInMs = Math.max(0, (batchOpenedAt ?? now()) + intervalMs - now());
|
|
127
142
|
batchTimer = scheduler.setTimeout(() => {
|
|
128
143
|
batchTimer = undefined;
|
|
144
|
+
batchTimerIntervalMs = undefined;
|
|
129
145
|
deliverBatch();
|
|
130
|
-
},
|
|
146
|
+
}, dueInMs);
|
|
131
147
|
}
|
|
132
148
|
/**
|
|
133
|
-
* Re-evaluate the
|
|
134
|
-
*
|
|
135
|
-
*
|
|
149
|
+
* Re-evaluate EVERYTHING the gate is holding against the config and
|
|
150
|
+
* suppression flag in force right now.
|
|
151
|
+
*
|
|
152
|
+
* Classification happens when an intent is published, but both energy and
|
|
153
|
+
* suppression move while a batch window is open. Without this pass a
|
|
154
|
+
* notification batched at Focus is delivered after a focus session starts,
|
|
155
|
+
* and one batched at Focus is surfaced at Rest with every channel disabled —
|
|
156
|
+
* the gate's own policy, applied to the wrong moment.
|
|
157
|
+
*
|
|
158
|
+
* Batched intents the current policy no longer admits are demoted to
|
|
159
|
+
* deferred; deferred intents it now admits are released immediately (not
|
|
160
|
+
* re-batched: they already waited once, and a second window would compound
|
|
161
|
+
* the delay). Nothing is dropped in either direction.
|
|
136
162
|
*/
|
|
137
|
-
function
|
|
138
|
-
if (disposed
|
|
163
|
+
function reconcilePending() {
|
|
164
|
+
if (disposed)
|
|
139
165
|
return;
|
|
140
166
|
const config = currentConfig();
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
167
|
+
if (batch.length > 0) {
|
|
168
|
+
const stillBatched = [];
|
|
169
|
+
for (const notification of batch) {
|
|
170
|
+
if (resolveNotificationOutcome(config, notification.priority, suppressed) === 'deferred') {
|
|
171
|
+
deferred.push(notification);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
stillBatched.push(notification);
|
|
175
|
+
}
|
|
146
176
|
}
|
|
147
|
-
|
|
148
|
-
|
|
177
|
+
batch = stillBatched;
|
|
178
|
+
}
|
|
179
|
+
const released = [];
|
|
180
|
+
if (deferred.length > 0) {
|
|
181
|
+
const stillDeferred = [];
|
|
182
|
+
for (const notification of deferred) {
|
|
183
|
+
if (resolveNotificationOutcome(config, notification.priority, suppressed) === 'deferred') {
|
|
184
|
+
stillDeferred.push(notification);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
released.push(notification);
|
|
188
|
+
}
|
|
149
189
|
}
|
|
190
|
+
deferred = stillDeferred;
|
|
191
|
+
}
|
|
192
|
+
if (batch.length === 0) {
|
|
193
|
+
clearBatchTimer();
|
|
194
|
+
batchOpenedAt = undefined;
|
|
150
195
|
}
|
|
151
|
-
|
|
196
|
+
else {
|
|
197
|
+
armBatchTimer(config.batchInterval);
|
|
198
|
+
}
|
|
199
|
+
// Delivered last, with the queues already settled: `onDeliver` is host code
|
|
200
|
+
// and may publish, suppress or flush re-entrantly.
|
|
152
201
|
deliver(released, 'released');
|
|
153
202
|
}
|
|
203
|
+
function deliverBatch() {
|
|
204
|
+
// Flushing a window is not an override of the active policy — only of the
|
|
205
|
+
// wait. Anything no longer admissible leaves through `deferred` instead.
|
|
206
|
+
reconcilePending();
|
|
207
|
+
clearBatchTimer();
|
|
208
|
+
if (batch.length === 0)
|
|
209
|
+
return;
|
|
210
|
+
const toDeliver = batch;
|
|
211
|
+
batch = [];
|
|
212
|
+
batchOpenedAt = undefined;
|
|
213
|
+
deliver(toDeliver, 'batch');
|
|
214
|
+
}
|
|
154
215
|
const unsubscribe = engine.subscribe(() => {
|
|
155
|
-
|
|
216
|
+
reconcilePending();
|
|
156
217
|
});
|
|
157
218
|
return {
|
|
158
219
|
publish(input) {
|
|
@@ -175,8 +236,9 @@ export function createNotificationGate(engine, options) {
|
|
|
175
236
|
deliver([notification], 'immediate');
|
|
176
237
|
break;
|
|
177
238
|
case 'batched':
|
|
239
|
+
batchOpenedAt ??= notification.createdAt;
|
|
178
240
|
batch.push(notification);
|
|
179
|
-
|
|
241
|
+
armBatchTimer(config.batchInterval);
|
|
180
242
|
break;
|
|
181
243
|
case 'deferred':
|
|
182
244
|
deferred.push(notification);
|
|
@@ -190,9 +252,9 @@ export function createNotificationGate(engine, options) {
|
|
|
190
252
|
if (suppressed === next)
|
|
191
253
|
return;
|
|
192
254
|
suppressed = next;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
255
|
+
// Both directions matter: entering suppression must pull an open batch
|
|
256
|
+
// back out of delivery, not just stop admitting new intents.
|
|
257
|
+
reconcilePending();
|
|
196
258
|
},
|
|
197
259
|
isSuppressed() {
|
|
198
260
|
return suppressed;
|
|
@@ -203,8 +265,9 @@ export function createNotificationGate(engine, options) {
|
|
|
203
265
|
flush() {
|
|
204
266
|
if (disposed)
|
|
205
267
|
return;
|
|
268
|
+
// Reconciles first, so flushing under suppression or a lower energy
|
|
269
|
+
// level defers the open batch rather than forcing it out.
|
|
206
270
|
deliverBatch();
|
|
207
|
-
releaseEligibleDeferred();
|
|
208
271
|
},
|
|
209
272
|
dispose() {
|
|
210
273
|
if (disposed)
|
|
@@ -215,6 +278,7 @@ export function createNotificationGate(engine, options) {
|
|
|
215
278
|
const pending = [...batch, ...deferred];
|
|
216
279
|
batch = [];
|
|
217
280
|
deferred = [];
|
|
281
|
+
batchOpenedAt = undefined;
|
|
218
282
|
deliver(pending, 'released');
|
|
219
283
|
disposed = true;
|
|
220
284
|
unsubscribe();
|
package/dist/gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAItD
|
|
1
|
+
{"version":3,"file":"gate.js","sourceRoot":"","sources":["../src/gate.ts"],"names":[],"mappings":"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"}
|
package/dist/levels.d.ts
CHANGED
|
@@ -19,4 +19,20 @@ export declare function isEnergySource(value: unknown): value is EnergySource;
|
|
|
19
19
|
export declare function isHigherEnergy(a: EnergyLevel, b: EnergyLevel): boolean;
|
|
20
20
|
/** Create an EnergyState for the current moment */
|
|
21
21
|
export declare function createEnergyState(level: EnergyLevel, source?: EnergySource, timestamp?: number, revision?: number, origin?: string): EnergyState;
|
|
22
|
+
/**
|
|
23
|
+
* Parse a value that came from OUTSIDE this engine — persisted JSON, a
|
|
24
|
+
* cross-context observation, a wire message — into an `EnergyState`.
|
|
25
|
+
*
|
|
26
|
+
* This is the interchange boundary, so it enforces the published JSON Schema
|
|
27
|
+
* exactly rather than approximately: `additionalProperties: false` means an
|
|
28
|
+
* object carrying an unknown key is not an `EnergyState`, and silently
|
|
29
|
+
* stripping the key would let two implementations disagree about what they
|
|
30
|
+
* just exchanged while both believe they conformed.
|
|
31
|
+
*
|
|
32
|
+
* Throws with a specific reason; callers decide whether that is fatal or a
|
|
33
|
+
* discard. Not exported from the package: the boundaries that need it are all
|
|
34
|
+
* inside this library, and the schema is what external producers validate
|
|
35
|
+
* against.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseExternalEnergyState(value: unknown): EnergyState;
|
|
22
38
|
//# sourceMappingURL=levels.d.ts.map
|
package/dist/levels.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"levels.d.ts","sourceRoot":"","sources":["../src/levels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAU/F,kFAAkF;AAClF,wBAAgB,kBAAkB,IAAI,MAAM,CAe3C;AAaD,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,QAAQ,CAAC,GAAG,OAAO,CAE3F;AAwFD,kEAAkE;AAClE,wBAAgB,eAAe,IAAI,aAAa,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAEhF;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,QAAQ,CAAC,qBAAqB,CAAC,CAIlF;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAKlE;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAEtE;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,YAAuB,EAC/B,SAAS,SAAa,EACtB,QAAQ,SAA2B,EACnC,MAAM,SAAwB,GAC7B,WAAW,
|
|
1
|
+
{"version":3,"file":"levels.d.ts","sourceRoot":"","sources":["../src/levels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAU/F,kFAAkF;AAClF,wBAAgB,kBAAkB,IAAI,MAAM,CAe3C;AAaD,eAAO,MAAM,iBAAiB,cAAc,CAAA;AAC5C,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,QAAQ,CAAC,GAAG,OAAO,CAE3F;AAwFD,kEAAkE;AAClE,wBAAgB,eAAe,IAAI,aAAa,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAEhF;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,QAAQ,CAAC,qBAAqB,CAAC,CAIlF;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAKlE;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAEtE;AAED,mDAAmD;AACnD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAClB,MAAM,GAAE,YAAuB,EAC/B,SAAS,SAAa,EACtB,QAAQ,SAA2B,EACnC,MAAM,SAAwB,GAC7B,WAAW,CA+Bb;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CA0CpE"}
|
package/dist/levels.js
CHANGED
|
@@ -156,7 +156,10 @@ export function createEnergyState(level, source = 'manual', timestamp = Date.now
|
|
|
156
156
|
if (!isEnergySource(source)) {
|
|
157
157
|
throw new Error(`Invalid energy source: ${String(source)}`);
|
|
158
158
|
}
|
|
159
|
-
|
|
159
|
+
// Integer, matching `spec/energy-state.schema.json`. A fractional timestamp
|
|
160
|
+
// is not representable in the interchange format, so accepting one here
|
|
161
|
+
// produces a state this implementation can hold but cannot legally publish.
|
|
162
|
+
if (!Number.isSafeInteger(timestamp) || timestamp < 0) {
|
|
160
163
|
throw new Error(`Invalid energy timestamp: ${String(timestamp)}`);
|
|
161
164
|
}
|
|
162
165
|
if (!Number.isSafeInteger(revision) || revision < 0) {
|
|
@@ -173,4 +176,54 @@ export function createEnergyState(level, source = 'manual', timestamp = Date.now
|
|
|
173
176
|
origin,
|
|
174
177
|
});
|
|
175
178
|
}
|
|
179
|
+
/** The exact key set `spec/energy-state.schema.json` permits. */
|
|
180
|
+
const ENERGY_STATE_KEYS = ['level', 'timestamp', 'source', 'revision', 'origin'];
|
|
181
|
+
/**
|
|
182
|
+
* Parse a value that came from OUTSIDE this engine — persisted JSON, a
|
|
183
|
+
* cross-context observation, a wire message — into an `EnergyState`.
|
|
184
|
+
*
|
|
185
|
+
* This is the interchange boundary, so it enforces the published JSON Schema
|
|
186
|
+
* exactly rather than approximately: `additionalProperties: false` means an
|
|
187
|
+
* object carrying an unknown key is not an `EnergyState`, and silently
|
|
188
|
+
* stripping the key would let two implementations disagree about what they
|
|
189
|
+
* just exchanged while both believe they conformed.
|
|
190
|
+
*
|
|
191
|
+
* Throws with a specific reason; callers decide whether that is fatal or a
|
|
192
|
+
* discard. Not exported from the package: the boundaries that need it are all
|
|
193
|
+
* inside this library, and the schema is what external producers validate
|
|
194
|
+
* against.
|
|
195
|
+
*/
|
|
196
|
+
export function parseExternalEnergyState(value) {
|
|
197
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
198
|
+
throw new Error(`Energy state must be an object, received: ${String(value)}`);
|
|
199
|
+
}
|
|
200
|
+
const keys = Object.keys(value);
|
|
201
|
+
const unknownKeys = keys.filter((key) => !ENERGY_STATE_KEYS.includes(key));
|
|
202
|
+
if (unknownKeys.length > 0) {
|
|
203
|
+
throw new Error(`Energy state has unknown properties: ${unknownKeys.join(', ')}`);
|
|
204
|
+
}
|
|
205
|
+
const missingKeys = ENERGY_STATE_KEYS.filter((key) => !keys.includes(key));
|
|
206
|
+
if (missingKeys.length > 0) {
|
|
207
|
+
throw new Error(`Energy state is missing properties: ${missingKeys.join(', ')}`);
|
|
208
|
+
}
|
|
209
|
+
const record = value;
|
|
210
|
+
if (!isEnergyLevel(record['level'])) {
|
|
211
|
+
throw new Error(`Invalid energy level: ${String(record['level'])}`);
|
|
212
|
+
}
|
|
213
|
+
if (!isEnergySource(record['source'])) {
|
|
214
|
+
throw new Error(`Invalid energy source: ${String(record['source'])}`);
|
|
215
|
+
}
|
|
216
|
+
if (typeof record['timestamp'] !== 'number') {
|
|
217
|
+
throw new TypeError(`Invalid energy timestamp: ${String(record['timestamp'])}`);
|
|
218
|
+
}
|
|
219
|
+
if (typeof record['revision'] !== 'number') {
|
|
220
|
+
throw new TypeError(`Invalid energy revision: ${String(record['revision'])}`);
|
|
221
|
+
}
|
|
222
|
+
if (typeof record['origin'] !== 'string') {
|
|
223
|
+
throw new TypeError(`Invalid energy origin: ${String(record['origin'])}`);
|
|
224
|
+
}
|
|
225
|
+
// Field-level invariants (integer/range/blank-origin) belong to the one
|
|
226
|
+
// constructor that enforces them for internally produced states too.
|
|
227
|
+
return createEnergyState(record['level'], record['source'], record['timestamp'], record['revision'], record['origin']);
|
|
228
|
+
}
|
|
176
229
|
//# sourceMappingURL=levels.js.map
|
package/dist/levels.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"levels.js","sourceRoot":"","sources":["../src/levels.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"levels.js","sourceRoot":"","sources":["../src/levels.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAkChE;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,SAAiB,GAAG,iBAAiB,CA4C/E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAyB1E"}
|
package/dist/persistence.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseExternalEnergyState } from './levels.js';
|
|
2
2
|
/*
|
|
3
3
|
* `null` means "nothing usable is stored", which the engine treats as a fresh
|
|
4
4
|
* install. Corrupt or schema-violating data reaches the same conclusion, but it
|
|
@@ -16,26 +16,17 @@ function parsePersistedState(source, raw) {
|
|
|
16
16
|
console.error(`[energy-system] Discarding unparseable persisted energy state (${source})`, err);
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
|
-
if (typeof parsed !== 'object' || parsed === null || !('level' in parsed)) {
|
|
20
|
-
console.error(`[energy-system] Discarding persisted energy state with unexpected shape (${source})`);
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
const obj = parsed;
|
|
24
|
-
if (!isEnergyLevel(obj['level']) ||
|
|
25
|
-
!isEnergySource(obj['source']) ||
|
|
26
|
-
typeof obj['timestamp'] !== 'number' ||
|
|
27
|
-
typeof obj['revision'] !== 'number' ||
|
|
28
|
-
typeof obj['origin'] !== 'string') {
|
|
29
|
-
console.error(`[energy-system] Discarding malformed persisted energy state (${source})`);
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
19
|
try {
|
|
33
|
-
|
|
20
|
+
// Shape, key set and field invariants are all one boundary rule, enforced
|
|
21
|
+
// to the letter of spec/energy-state.schema.json — including its
|
|
22
|
+
// `additionalProperties: false`. Storage written by a newer or foreign
|
|
23
|
+
// producer is rejected, not quietly trimmed to fit.
|
|
24
|
+
return parseExternalEnergyState(parsed);
|
|
34
25
|
}
|
|
35
26
|
catch (err) {
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
console.error(`[energy-system] Discarding
|
|
27
|
+
// Distinct from the unparseable case above: the bytes were JSON, the
|
|
28
|
+
// content was not an EnergyState. The thrown error carries which rule broke.
|
|
29
|
+
console.error(`[energy-system] Discarding malformed persisted energy state (${source})`, err);
|
|
39
30
|
return null;
|
|
40
31
|
}
|
|
41
32
|
}
|
|
@@ -90,18 +81,20 @@ export function localStoragePersistence(key = 'energy-state') {
|
|
|
90
81
|
* Useful for tests, SSR, or ephemeral sessions.
|
|
91
82
|
*/
|
|
92
83
|
export function memoryPersistence(initial) {
|
|
93
|
-
let stored = initial === undefined
|
|
94
|
-
? null
|
|
95
|
-
: createEnergyState(initial.level, initial.source, initial.timestamp, initial.revision, initial.origin);
|
|
84
|
+
let stored = initial === undefined ? null : parseExternalEnergyState(initial);
|
|
96
85
|
const listeners = new Set();
|
|
97
86
|
return {
|
|
98
87
|
async load() {
|
|
99
88
|
return stored;
|
|
100
89
|
},
|
|
101
90
|
async save(state) {
|
|
102
|
-
stored
|
|
91
|
+
// Re-parsed rather than stored by reference: this adapter is the one
|
|
92
|
+
// observers read back from, so what it hands out must satisfy the
|
|
93
|
+
// interchange contract exactly, whoever called save().
|
|
94
|
+
const next = parseExternalEnergyState(state);
|
|
95
|
+
stored = next;
|
|
103
96
|
for (const listener of listeners) {
|
|
104
|
-
listener(
|
|
97
|
+
listener(next);
|
|
105
98
|
}
|
|
106
99
|
},
|
|
107
100
|
observe(onState) {
|
package/dist/persistence.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kumbatio/energy-system",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Framework-agnostic TypeScript library for energy-aware application behavior.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,27 +41,29 @@
|
|
|
41
41
|
"./api-surface.json": "./api-surface.json",
|
|
42
42
|
"./package.json": "./package.json",
|
|
43
43
|
"./conformance.json": "./conformance.json",
|
|
44
|
-
"./spec/energy-state.schema.json": "./spec/energy-state.schema.json"
|
|
44
|
+
"./spec/energy-state.schema.json": "./spec/energy-state.schema.json",
|
|
45
|
+
"./spec/conformance.schema.json": "./spec/conformance.schema.json"
|
|
45
46
|
},
|
|
46
47
|
"publishConfig": {
|
|
47
48
|
"access": "public"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
|
-
"@happy-dom/global-registrator": "^20.11.
|
|
51
|
-
"@types/node": "^26.
|
|
51
|
+
"@happy-dom/global-registrator": "^20.11.2",
|
|
52
|
+
"@types/node": "^26.2.0",
|
|
52
53
|
"@types/react": "^19.2.18",
|
|
53
54
|
"@types/react-dom": "^19.2.4",
|
|
55
|
+
"ajv": "8.20.0",
|
|
54
56
|
"oxfmt": "0.62.0",
|
|
55
57
|
"oxlint": "1.77.0",
|
|
56
58
|
"oxlint-tsgolint": "^7.0.2001",
|
|
57
59
|
"react": "^19.2.8",
|
|
58
60
|
"react-dom": "^19.2.8",
|
|
59
|
-
"tsx": "^4.23.
|
|
61
|
+
"tsx": "^4.23.12",
|
|
60
62
|
"typescript": "^7.0.2"
|
|
61
63
|
},
|
|
62
64
|
"peerDependencies": {
|
|
63
|
-
"@types/react": ">=19",
|
|
64
|
-
"react": ">=19"
|
|
65
|
+
"@types/react": ">=19.2.0",
|
|
66
|
+
"react": ">=19.2.0"
|
|
65
67
|
},
|
|
66
68
|
"peerDependenciesMeta": {
|
|
67
69
|
"@types/react": {
|
|
@@ -75,18 +77,21 @@
|
|
|
75
77
|
"node": ">=24.19.0"
|
|
76
78
|
},
|
|
77
79
|
"scripts": {
|
|
78
|
-
"build": "tsc -p tsconfig.build.json
|
|
80
|
+
"build:ts": "tsc -p tsconfig.build.json",
|
|
81
|
+
"build": "pnpm run build:ts && pnpm run api:surface && pnpm run conformance",
|
|
79
82
|
"api:surface": "tsx scripts/extract-api-surface.ts",
|
|
83
|
+
"api:surface:check": "tsx scripts/extract-api-surface.ts --check",
|
|
80
84
|
"check-types": "tsc --noEmit -p tsconfig.json",
|
|
81
|
-
"test": "pnpm run build && node --test --import tsx",
|
|
85
|
+
"test": "pnpm run build:ts && node --test --import tsx",
|
|
82
86
|
"validate": "pnpm run format:check && pnpm run lint && pnpm run check-types && pnpm run test && pnpm run pack:dry-run",
|
|
83
|
-
"pack:dry-run": "
|
|
87
|
+
"pack:dry-run": "pnpm pack --dry-run",
|
|
84
88
|
"format": "oxfmt --write .",
|
|
85
89
|
"format:check": "oxfmt --check .",
|
|
86
90
|
"lint": "oxlint --import-plugin --type-aware --type-check --vitest-plugin .",
|
|
87
91
|
"lint:fix": "oxlint --import-plugin --type-aware --type-check --vitest-plugin --fix . && oxfmt --write .",
|
|
88
92
|
"lint:fix-unsafe": "oxlint --import-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions --fix-dangerously . && oxfmt --write .",
|
|
89
93
|
"check:fix-suggestions": "oxlint --import-plugin --type-aware --type-check --vitest-plugin --fix --fix-suggestions . && oxfmt --write .",
|
|
90
|
-
"conformance": "TZ=UTC tsx scripts/extract-conformance.ts"
|
|
94
|
+
"conformance": "TZ=UTC tsx scripts/extract-conformance.ts",
|
|
95
|
+
"conformance:check": "TZ=UTC tsx scripts/extract-conformance.ts --check"
|
|
91
96
|
}
|
|
92
97
|
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://kumbat.io/spec/conformance.schema.json",
|
|
4
|
+
"title": "EnergyConformance",
|
|
5
|
+
"description": "The shape of conformance.json: the executable half of SPEC.md. A port in any language passes by loading that file and asserting its own output against each vector. This schema is what lets that port know the file it loaded is the file it expects — vectors that silently changed shape are worse than vectors that failed to load. scripts/extract-conformance.ts validates its own output against this schema before writing, so the two cannot drift apart unnoticed.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"version",
|
|
10
|
+
"timezone",
|
|
11
|
+
"note",
|
|
12
|
+
"levels",
|
|
13
|
+
"cycle",
|
|
14
|
+
"strategies",
|
|
15
|
+
"presence",
|
|
16
|
+
"decisions",
|
|
17
|
+
"deferral",
|
|
18
|
+
"reconciliation",
|
|
19
|
+
"metrics",
|
|
20
|
+
"externalLevelMapping"
|
|
21
|
+
],
|
|
22
|
+
"$defs": {
|
|
23
|
+
"level": {
|
|
24
|
+
"description": "The five discrete capacities. Never a continuum.",
|
|
25
|
+
"type": "integer",
|
|
26
|
+
"enum": [0, 25, 50, 75, 100]
|
|
27
|
+
},
|
|
28
|
+
"presence": {
|
|
29
|
+
"description": "What a projected element does at a level. `unmounted` is a host/adapter choice rather than a table value, so it does not appear in these vectors.",
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["visible", "muted", "hidden", "unmounted"]
|
|
32
|
+
},
|
|
33
|
+
"strategyTable": {
|
|
34
|
+
"description": "One strategy's resolved config at every level. Keys are strings because JSON object keys are strings, and all five must be present: a gap in a port's table falls through silently at exactly the level it mishandles.",
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"required": ["0", "25", "50", "75", "100"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"0": { "type": "object", "minProperties": 1 },
|
|
40
|
+
"25": { "type": "object", "minProperties": 1 },
|
|
41
|
+
"50": { "type": "object", "minProperties": 1 },
|
|
42
|
+
"75": { "type": "object", "minProperties": 1 },
|
|
43
|
+
"100": { "type": "object", "minProperties": 1 }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"presenceRow": {
|
|
47
|
+
"description": "What each level resolves to for one helper threshold.",
|
|
48
|
+
"type": "object",
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"required": ["0", "25", "50", "75", "100"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"0": { "$ref": "#/$defs/presence" },
|
|
53
|
+
"25": { "$ref": "#/$defs/presence" },
|
|
54
|
+
"50": { "$ref": "#/$defs/presence" },
|
|
55
|
+
"75": { "$ref": "#/$defs/presence" },
|
|
56
|
+
"100": { "$ref": "#/$defs/presence" }
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"presenceTable": {
|
|
60
|
+
"description": "Keyed by the helper's threshold level, then by the current level.",
|
|
61
|
+
"type": "object",
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"required": ["0", "25", "50", "75", "100"],
|
|
64
|
+
"properties": {
|
|
65
|
+
"0": { "$ref": "#/$defs/presenceRow" },
|
|
66
|
+
"25": { "$ref": "#/$defs/presenceRow" },
|
|
67
|
+
"50": { "$ref": "#/$defs/presenceRow" },
|
|
68
|
+
"75": { "$ref": "#/$defs/presenceRow" },
|
|
69
|
+
"100": { "$ref": "#/$defs/presenceRow" }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"instant": {
|
|
73
|
+
"description": "An epoch-millisecond instant paired with its ISO rendering, so a port can compare either without re-deriving one from the other.",
|
|
74
|
+
"type": "object",
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"required": ["epochMs", "iso"],
|
|
77
|
+
"properties": {
|
|
78
|
+
"epochMs": { "type": "integer", "minimum": 0 },
|
|
79
|
+
"iso": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"properties": {
|
|
87
|
+
"$schema": {
|
|
88
|
+
"description": "Reference to this schema, relative to the package root so it resolves inside the published tarball as well as in the repository.",
|
|
89
|
+
"type": "string",
|
|
90
|
+
"const": "./spec/conformance.schema.json"
|
|
91
|
+
},
|
|
92
|
+
"version": {
|
|
93
|
+
"description": "The package version these vectors were generated from. Always equal to api-surface.json's version.",
|
|
94
|
+
"type": "string",
|
|
95
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+"
|
|
96
|
+
},
|
|
97
|
+
"timezone": {
|
|
98
|
+
"description": "The zone the deferral vectors were computed in. The presets are local-time by design, so the vectors only mean anything alongside the zone that produced them.",
|
|
99
|
+
"const": "UTC"
|
|
100
|
+
},
|
|
101
|
+
"note": { "type": "string", "minLength": 1 },
|
|
102
|
+
|
|
103
|
+
"levels": {
|
|
104
|
+
"description": "Level definitions, including the cognitive profile a port must reproduce.",
|
|
105
|
+
"type": "array",
|
|
106
|
+
"minItems": 5,
|
|
107
|
+
"maxItems": 5,
|
|
108
|
+
"items": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"required": ["value", "key", "label", "description", "cognitiveProfile"],
|
|
112
|
+
"properties": {
|
|
113
|
+
"value": { "$ref": "#/$defs/level" },
|
|
114
|
+
"key": { "type": "string", "minLength": 1 },
|
|
115
|
+
"label": { "type": "string", "minLength": 1 },
|
|
116
|
+
"description": { "type": "string", "minLength": 1 },
|
|
117
|
+
"cognitiveProfile": {
|
|
118
|
+
"type": "object",
|
|
119
|
+
"additionalProperties": false,
|
|
120
|
+
"required": [
|
|
121
|
+
"decisionCapacity",
|
|
122
|
+
"focusDuration",
|
|
123
|
+
"taskComplexity",
|
|
124
|
+
"interruptionTolerance"
|
|
125
|
+
],
|
|
126
|
+
"properties": {
|
|
127
|
+
"decisionCapacity": { "type": "string", "minLength": 1 },
|
|
128
|
+
"focusDuration": { "type": "string", "minLength": 1 },
|
|
129
|
+
"taskComplexity": { "type": "string", "minLength": 1 },
|
|
130
|
+
"interruptionTolerance": { "type": "string", "minLength": 1 }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
"cycle": {
|
|
138
|
+
"description": "100 -> 75 -> 50 -> 25 -> 0 -> 100.",
|
|
139
|
+
"type": "array",
|
|
140
|
+
"minItems": 5,
|
|
141
|
+
"maxItems": 5,
|
|
142
|
+
"items": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"additionalProperties": false,
|
|
145
|
+
"required": ["from", "to"],
|
|
146
|
+
"properties": {
|
|
147
|
+
"from": { "$ref": "#/$defs/level" },
|
|
148
|
+
"to": { "$ref": "#/$defs/level" }
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
"strategies": {
|
|
154
|
+
"description": "Every built-in strategy's resolved config at every level. Config shapes differ per strategy; their field-level meaning is fixed by SPEC.md and re-asserted by replaying each table through the public API in test/conformance.test.ts.",
|
|
155
|
+
"type": "object",
|
|
156
|
+
"additionalProperties": false,
|
|
157
|
+
"required": [
|
|
158
|
+
"ui-visibility",
|
|
159
|
+
"notifications",
|
|
160
|
+
"task-complexity",
|
|
161
|
+
"interaction-forgiveness",
|
|
162
|
+
"deferral",
|
|
163
|
+
"autonomy",
|
|
164
|
+
"demand-admission"
|
|
165
|
+
],
|
|
166
|
+
"properties": {
|
|
167
|
+
"ui-visibility": { "$ref": "#/$defs/strategyTable" },
|
|
168
|
+
"notifications": { "$ref": "#/$defs/strategyTable" },
|
|
169
|
+
"task-complexity": { "$ref": "#/$defs/strategyTable" },
|
|
170
|
+
"interaction-forgiveness": { "$ref": "#/$defs/strategyTable" },
|
|
171
|
+
"deferral": { "$ref": "#/$defs/strategyTable" },
|
|
172
|
+
"autonomy": { "$ref": "#/$defs/strategyTable" },
|
|
173
|
+
"demand-admission": { "$ref": "#/$defs/strategyTable" }
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
"presence": {
|
|
178
|
+
"description": "Presence helpers, which are pure maps over the level set.",
|
|
179
|
+
"type": "object",
|
|
180
|
+
"additionalProperties": false,
|
|
181
|
+
"required": ["atOrAbove", "atOrAboveMuted", "atOrBelow"],
|
|
182
|
+
"properties": {
|
|
183
|
+
"atOrAbove": { "$ref": "#/$defs/presenceTable" },
|
|
184
|
+
"atOrAboveMuted": { "$ref": "#/$defs/presenceTable" },
|
|
185
|
+
"atOrBelow": { "$ref": "#/$defs/presenceTable" }
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
"decisions": {
|
|
190
|
+
"description": "The full decision matrix of both pure gating functions.",
|
|
191
|
+
"type": "object",
|
|
192
|
+
"additionalProperties": false,
|
|
193
|
+
"required": ["notification", "demand"],
|
|
194
|
+
"properties": {
|
|
195
|
+
"notification": {
|
|
196
|
+
"type": "array",
|
|
197
|
+
"minItems": 1,
|
|
198
|
+
"items": {
|
|
199
|
+
"type": "object",
|
|
200
|
+
"additionalProperties": false,
|
|
201
|
+
"required": ["level", "priority", "suppressed", "outcome"],
|
|
202
|
+
"properties": {
|
|
203
|
+
"level": { "$ref": "#/$defs/level" },
|
|
204
|
+
"priority": { "type": "string", "enum": ["normal", "high", "critical"] },
|
|
205
|
+
"suppressed": { "type": "boolean" },
|
|
206
|
+
"outcome": { "type": "string", "enum": ["delivered", "batched", "deferred"] }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"demand": {
|
|
211
|
+
"type": "array",
|
|
212
|
+
"minItems": 1,
|
|
213
|
+
"items": {
|
|
214
|
+
"type": "object",
|
|
215
|
+
"additionalProperties": false,
|
|
216
|
+
"required": ["level", "originatorTier", "bearsObligation", "confidence", "outcome"],
|
|
217
|
+
"properties": {
|
|
218
|
+
"level": { "$ref": "#/$defs/level" },
|
|
219
|
+
"originatorTier": { "type": "string", "enum": ["exempt", "known", "unknown"] },
|
|
220
|
+
"bearsObligation": { "type": "boolean" },
|
|
221
|
+
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
222
|
+
"outcome": {
|
|
223
|
+
"type": "object",
|
|
224
|
+
"additionalProperties": false,
|
|
225
|
+
"required": ["admission", "acknowledgment", "reason"],
|
|
226
|
+
"properties": {
|
|
227
|
+
"admission": { "type": "string", "enum": ["live", "acknowledge", "silent"] },
|
|
228
|
+
"acknowledgment": {
|
|
229
|
+
"description": "null when the policy admits or drops without acknowledging.",
|
|
230
|
+
"type": ["object", "null"],
|
|
231
|
+
"additionalProperties": false,
|
|
232
|
+
"required": ["detail", "allowGeneratedContent"],
|
|
233
|
+
"properties": {
|
|
234
|
+
"detail": { "type": "string", "enum": ["full", "brief", "minimal"] },
|
|
235
|
+
"allowGeneratedContent": { "type": "boolean" }
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
"reason": {
|
|
239
|
+
"type": "string",
|
|
240
|
+
"enum": [
|
|
241
|
+
"exempt-originator",
|
|
242
|
+
"tier-admitted",
|
|
243
|
+
"acknowledged",
|
|
244
|
+
"no-obligation",
|
|
245
|
+
"below-confidence"
|
|
246
|
+
]
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
"deferral": {
|
|
257
|
+
"description": "Deferral arithmetic, resolved against fixed reference instants. Local-time by design, hence the `timezone` field above.",
|
|
258
|
+
"type": "object",
|
|
259
|
+
"additionalProperties": false,
|
|
260
|
+
"required": ["morningHour", "eveningHour", "references"],
|
|
261
|
+
"properties": {
|
|
262
|
+
"morningHour": { "type": "integer", "minimum": 0, "maximum": 23 },
|
|
263
|
+
"eveningHour": { "type": "integer", "minimum": 0, "maximum": 23 },
|
|
264
|
+
"references": {
|
|
265
|
+
"type": "array",
|
|
266
|
+
"minItems": 1,
|
|
267
|
+
"items": {
|
|
268
|
+
"type": "object",
|
|
269
|
+
"additionalProperties": false,
|
|
270
|
+
"required": ["label", "iso", "epochMs", "resolved"],
|
|
271
|
+
"properties": {
|
|
272
|
+
"label": { "type": "string", "minLength": 1 },
|
|
273
|
+
"iso": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$"
|
|
276
|
+
},
|
|
277
|
+
"epochMs": { "type": "integer", "minimum": 0 },
|
|
278
|
+
"resolved": {
|
|
279
|
+
"description": "Every shipped preset, resolved against this reference instant.",
|
|
280
|
+
"type": "object",
|
|
281
|
+
"additionalProperties": false,
|
|
282
|
+
"required": [
|
|
283
|
+
"in-1-hour",
|
|
284
|
+
"this-evening",
|
|
285
|
+
"tomorrow-morning",
|
|
286
|
+
"next-workday",
|
|
287
|
+
"next-monday"
|
|
288
|
+
],
|
|
289
|
+
"properties": {
|
|
290
|
+
"in-1-hour": { "$ref": "#/$defs/instant" },
|
|
291
|
+
"this-evening": { "$ref": "#/$defs/instant" },
|
|
292
|
+
"tomorrow-morning": { "$ref": "#/$defs/instant" },
|
|
293
|
+
"next-workday": { "$ref": "#/$defs/instant" },
|
|
294
|
+
"next-monday": { "$ref": "#/$defs/instant" }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
"reconciliation": {
|
|
304
|
+
"description": "The ordering rule two independent producers converge on. Each pair is stated in both directions: a rule that is not antisymmetric does not converge.",
|
|
305
|
+
"type": "object",
|
|
306
|
+
"additionalProperties": false,
|
|
307
|
+
"required": ["sourcePriority", "pairs"],
|
|
308
|
+
"properties": {
|
|
309
|
+
"sourcePriority": {
|
|
310
|
+
"type": "object",
|
|
311
|
+
"additionalProperties": false,
|
|
312
|
+
"required": ["manual", "scheduled", "inferred"],
|
|
313
|
+
"properties": {
|
|
314
|
+
"manual": { "const": 3 },
|
|
315
|
+
"scheduled": { "const": 2 },
|
|
316
|
+
"inferred": { "const": 1 }
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"pairs": {
|
|
320
|
+
"type": "array",
|
|
321
|
+
"minItems": 1,
|
|
322
|
+
"items": {
|
|
323
|
+
"type": "object",
|
|
324
|
+
"additionalProperties": false,
|
|
325
|
+
"required": ["rule", "candidate", "current", "expected", "expectedReversed"],
|
|
326
|
+
"properties": {
|
|
327
|
+
"rule": { "type": "string", "minLength": 1 },
|
|
328
|
+
"candidate": { "$ref": "https://kumbat.io/spec/energy-state.schema.json" },
|
|
329
|
+
"current": { "$ref": "https://kumbat.io/spec/energy-state.schema.json" },
|
|
330
|
+
"expected": { "type": "boolean" },
|
|
331
|
+
"expectedReversed": { "type": "boolean" }
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
"metrics": {
|
|
339
|
+
"description": "Derived metrics at a fixed state age, so a port checks its arithmetic rather than its clock.",
|
|
340
|
+
"type": "array",
|
|
341
|
+
"minItems": 5,
|
|
342
|
+
"maxItems": 5,
|
|
343
|
+
"items": {
|
|
344
|
+
"type": "object",
|
|
345
|
+
"additionalProperties": false,
|
|
346
|
+
"required": ["level", "stateTimestamp", "now", "metrics"],
|
|
347
|
+
"properties": {
|
|
348
|
+
"level": { "$ref": "#/$defs/level" },
|
|
349
|
+
"stateTimestamp": { "type": "integer", "minimum": 0 },
|
|
350
|
+
"now": { "type": "integer", "minimum": 0 },
|
|
351
|
+
"metrics": {
|
|
352
|
+
"type": "object",
|
|
353
|
+
"additionalProperties": false,
|
|
354
|
+
"required": [
|
|
355
|
+
"stateAgeMs",
|
|
356
|
+
"stateAgeMinutes",
|
|
357
|
+
"expectedProductivityWindowMinutes",
|
|
358
|
+
"suggestedBreakIntervalMinutes",
|
|
359
|
+
"recommendedTaskComplexity",
|
|
360
|
+
"sustainable"
|
|
361
|
+
],
|
|
362
|
+
"properties": {
|
|
363
|
+
"stateAgeMs": { "type": "integer", "minimum": 0 },
|
|
364
|
+
"stateAgeMinutes": { "type": "integer", "minimum": 0 },
|
|
365
|
+
"expectedProductivityWindowMinutes": { "type": "integer", "minimum": 0 },
|
|
366
|
+
"suggestedBreakIntervalMinutes": { "type": "integer", "minimum": 0 },
|
|
367
|
+
"recommendedTaskComplexity": { "type": "string", "minLength": 1 },
|
|
368
|
+
"sustainable": { "type": "boolean" },
|
|
369
|
+
"recoveryHintMinutes": {
|
|
370
|
+
"description": "Present only at the levels that have a recovery hint (50 and below); absent, not null, where there is nothing to suggest.",
|
|
371
|
+
"type": "integer",
|
|
372
|
+
"minimum": 0
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
"externalLevelMapping": {
|
|
381
|
+
"description": "Percentages an external model might hand over, including both midpoints — 12.5 and 37.5 sit exactly between two levels, which is where rounding implementations disagree.",
|
|
382
|
+
"type": "array",
|
|
383
|
+
"minItems": 1,
|
|
384
|
+
"items": {
|
|
385
|
+
"type": "object",
|
|
386
|
+
"additionalProperties": false,
|
|
387
|
+
"required": ["input", "level"],
|
|
388
|
+
"properties": {
|
|
389
|
+
"input": { "type": "number" },
|
|
390
|
+
"level": { "$ref": "#/$defs/level" }
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|