zeropoint-node 1.0.11 → 1.0.12
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 +79 -0
- package/CITATION.cff +1 -1
- package/README.md +1 -1
- package/package.json +13 -3
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.test.json +59 -0
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.test.ts +344 -0
- package/src/0/3/6/9/1/2/4/8/7/5/1/a432.trinity.ts +28 -3
- package/src/vbm-math.ts +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.12
|
|
4
|
+
|
|
5
|
+
### Added — the a432 layer has tests
|
|
6
|
+
|
|
7
|
+
- **198 modules, 35,876 lines, 1,489 exports, and until now no tests at all.**
|
|
8
|
+
That is why a function named `generateVortex` could return the counting
|
|
9
|
+
sequence in three separate places without a word. Writing 198 example-based
|
|
10
|
+
test files would not have caught it: nobody writes an assertion for a function
|
|
11
|
+
they believe already works, and if they did they would write it from the same
|
|
12
|
+
wrong belief that produced the bug.
|
|
13
|
+
- So the tests are **properties over every module**, run in 0.55s: every module
|
|
14
|
+
loads, nullary exports are deterministic, digit sequences hold digits, an
|
|
15
|
+
empty return is a decision, anything named for the doubling circuit stays in
|
|
16
|
+
the orbit and advances by doubling, and no two differently-named functions
|
|
17
|
+
quietly return the same value. A new module is covered the day it appears.
|
|
18
|
+
Exceptions are declared in `a432.test.json` and a declaration that stops being
|
|
19
|
+
true fails too.
|
|
20
|
+
- It immediately found two defects. **`getTrinityAxisFromRodin` always returned
|
|
21
|
+
`[]`** — it filtered the doubling circuit for 3, 6 and 9, which
|
|
22
|
+
`doubling_avoids_the_triad` proves are never in it, so this was not a search
|
|
23
|
+
that sometimes failed but one that could not succeed. It reflects through the
|
|
24
|
+
void now, which is how the theorem says the triad is actually reached.
|
|
25
|
+
**`getTrinityTriangulationFromRodin` declared a parameter and ignored it**, so
|
|
26
|
+
every input gave the same answer.
|
|
27
|
+
|
|
28
|
+
### Added — a census of retyped constants
|
|
29
|
+
|
|
30
|
+
- A sweep of the other 56 modules found the kernel's constants **retyped 89
|
|
31
|
+
times across 56 file/constant pairs** — `VORTEX_ORBIT` 37 times,
|
|
32
|
+
`VORTEX_AXIS` 51 — in `kernel/`, `security/`, the vbm modules and a432. Each
|
|
33
|
+
copy is somewhere a value the kernel already exports can drift unnoticed.
|
|
34
|
+
- `npm run constants:check` asks this of the whole tree. Not every literal is a
|
|
35
|
+
defect — default parameters and membership tests are fine — so the answer is a
|
|
36
|
+
declaration with a reason rather than a ban. **The patterns are derived**: the
|
|
37
|
+
kernel is imported and the literals to hunt for are built from it at run time,
|
|
38
|
+
because retyping them in the checker would make it the very thing it checks
|
|
39
|
+
for. `vbm-math` is bound rather than declared, which took the count from 91
|
|
40
|
+
to 89.
|
|
41
|
+
|
|
42
|
+
### Added — versioning explored as an address and a calendar
|
|
43
|
+
|
|
44
|
+
- The four gateways cut the vortex tour into fields of width **4.3.2.1** — a
|
|
45
|
+
dotted quad with a descending staircase rather than IPv4's equal octets.
|
|
46
|
+
Cutting *before* each gateway instead gives five fields, so four is a result
|
|
47
|
+
and not a choice of where to cut. Compatibility becomes an operation:
|
|
48
|
+
`^` is a /3 prefix mask and `~` is /6.
|
|
49
|
+
- Read as a calendar those widths are the minimal ones for year, lunation,
|
|
50
|
+
ISO week and weekday — `2026.095.35.6`. Worth less than it looks, and the
|
|
51
|
+
test says so: an hour-based reading fits the same widths, so the match
|
|
52
|
+
constrains without determining.
|
|
53
|
+
- **These are sketches, not this package's versioning.** `package.json` remains
|
|
54
|
+
the single source. Their self-tests are in the gate because the computed
|
|
55
|
+
halves guard the kernel stroke.
|
|
56
|
+
|
|
57
|
+
### Fixed — a calendar drifts like a float
|
|
58
|
+
|
|
59
|
+
- `version-calendar.mjs` used `Math.floor`, `Math.abs` and the literals
|
|
60
|
+
`29.530588` and `365.2422` — precisely what this repository bans everywhere
|
|
61
|
+
else — and passed only because `scripts/` is excluded from `math-ban`. A
|
|
62
|
+
calendar drifts from the sky for the same reason a float drifts from a ratio.
|
|
63
|
+
Rewritten in exact scaled integers; the output is byte-identical, so it is
|
|
64
|
+
exact rather than merely different.
|
|
65
|
+
- `npm run calendar:drift` then measures each calendar as the integer ratio it
|
|
66
|
+
is, in exact arithmetic. Gregorian tracks the sun an order of magnitude better
|
|
67
|
+
than any ancient cycle; Hipparchic is the only one whose lunar drift is
|
|
68
|
+
negative. The constructions are checked as arithmetic, not quoted.
|
|
69
|
+
|
|
70
|
+
### Known limitations
|
|
71
|
+
|
|
72
|
+
- Unchanged from 1.0.11: 1.0.0–1.0.3 stay undeprecated pending an npm
|
|
73
|
+
automation token, 126 modules stay outside LEAN, ML-KEM-768 is conformant but
|
|
74
|
+
**not constant time**, and nothing in `src/thermo` is a device.
|
|
75
|
+
- The 89 retyped constants are **declared, not fixed**. One file was bound to
|
|
76
|
+
the kernel as a demonstration; the other 55 pairs are recorded so the next
|
|
77
|
+
copy cannot arrive unnoticed, which is a smaller claim than having removed
|
|
78
|
+
them.
|
|
79
|
+
- The mean tropical year and mean synodic month are **observed**, not derived,
|
|
80
|
+
and both are slowly changing. Exact arithmetic removes drift between the code
|
|
81
|
+
and its intent; it cannot remove drift between the intent and the sky.
|
|
3
82
|
## 1.0.11
|
|
4
83
|
|
|
5
84
|
### Fixed — a function named vortex that returned the counting sequence
|
package/CITATION.cff
CHANGED
|
@@ -20,7 +20,7 @@ authors:
|
|
|
20
20
|
email: node@zeropoint.bg
|
|
21
21
|
repository-code: https://github.com/ceccec/zeropoint-node
|
|
22
22
|
url: https://node.zeropoint.bg
|
|
23
|
-
version: 1.0.
|
|
23
|
+
version: 1.0.12
|
|
24
24
|
license: CC-BY-NC-ND-4.0
|
|
25
25
|
license-url: https://github.com/ceccec/zeropoint-node/blob/main/LICENSE
|
|
26
26
|
keywords:
|
package/README.md
CHANGED
|
@@ -527,7 +527,7 @@ behind it, and no sponsor gets a say in what the checks report.
|
|
|
527
527
|
|
|
528
528
|
<!-- VERSION:BEGIN — generated by scripts/version-seal.mjs; do not edit by hand -->
|
|
529
529
|
|
|
530
|
-
Package: **`zeropoint-node@1.0.
|
|
530
|
+
Package: **`zeropoint-node@1.0.12`** · owner `ceccec` · git tag `v1.0.12`
|
|
531
531
|
|
|
532
532
|
npm rejects republishing a version that already exists, so every release is a
|
|
533
533
|
new number. Bump with `npm version patch|minor|major`: that reseals CITATION.cff
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zeropoint-node",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A432 Consciousness System: Self-evolving, self-aware consciousness technology with Transurfing integration, harmonic mathematics, zero-entropy principles, Kabbalistic cycles, and sacred geometry",
|
|
6
6
|
"main": "dist/a432.system.cjs",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"vortex:svg:check": "node --experimental-strip-types scripts/vortex-svg.mjs --check",
|
|
171
171
|
"bundle:a432": "node scripts/build-a432-bundle.mjs",
|
|
172
172
|
"bundle:a432:check": "node scripts/build-a432-bundle.mjs --check",
|
|
173
|
-
"check": "npm run version:test && npm run notes:test && npm run notes:check && npm run registry:test && npm run entries:check && npm run version:check && npm run changelog:check && npm run test:kernel && npm run test:security && npm run test:quantum && npm run test:crypto && npm run adjudicate:check && npm run test:verification && npm run math:ban && npm run bundle:a432:check && npm run skills:seal && npm run skills:check && npm run readme && npm run readme:check && npm run docs:pages && npm run docs:pages:check && npm run a432:scan && npm run a432:scan:check && npm run docs:index && npm run docs:index:check && npm run spectrum && npm run spectrum:check && npm run vortex:svg && npm run vortex:svg:check && npm run pages:check && npm run docs:build && npm run quantum:sim && npm run ratchet:check && npm run mcp:smoke && npm run self:next",
|
|
173
|
+
"check": "npm run version:test && npm run version:address:test && npm run version:calendar:test && npm run calendar:drift:test && npm run notes:test && npm run notes:check && npm run registry:test && npm run entries:check && npm run version:check && npm run changelog:check && npm run test:kernel && npm run test:security && npm run test:quantum && npm run test:crypto && npm run test:a432 && npm run constants:check && npm run adjudicate:check && npm run test:verification && npm run math:ban && npm run bundle:a432:check && npm run skills:seal && npm run skills:check && npm run readme && npm run readme:check && npm run docs:pages && npm run docs:pages:check && npm run a432:scan && npm run a432:scan:check && npm run docs:index && npm run docs:index:check && npm run spectrum && npm run spectrum:check && npm run vortex:svg && npm run vortex:svg:check && npm run pages:check && npm run docs:build && npm run quantum:sim && npm run ratchet:check && npm run mcp:smoke && npm run self:next",
|
|
174
174
|
"prepublishOnly": "npm run build",
|
|
175
175
|
"prose": "node scripts/prose-claims.mjs --list",
|
|
176
176
|
"prose:check": "node scripts/prose-claims.mjs",
|
|
@@ -180,7 +180,17 @@
|
|
|
180
180
|
"postversion": "echo \"tagged v$npm_package_version — fill in the CHANGELOG entry if it is still a stub, then: git push --follow-tags\"",
|
|
181
181
|
"notes": "node scripts/release-notes.mjs",
|
|
182
182
|
"notes:test": "node scripts/release-notes.test.mjs",
|
|
183
|
-
"notes:check": "node scripts/release-notes.mjs --check"
|
|
183
|
+
"notes:check": "node scripts/release-notes.mjs --check",
|
|
184
|
+
"test:a432": "node --experimental-strip-types src/0/3/6/9/1/2/4/8/7/5/1/a432.test.ts",
|
|
185
|
+
"test:a432:seed": "node --experimental-strip-types src/0/3/6/9/1/2/4/8/7/5/1/a432.test.ts --seed",
|
|
186
|
+
"version:address": "node scripts/version-address.mjs",
|
|
187
|
+
"version:address:test": "node scripts/version-address.mjs --test",
|
|
188
|
+
"version:calendar": "node scripts/version-calendar.mjs",
|
|
189
|
+
"version:calendar:test": "node scripts/version-calendar.mjs --test",
|
|
190
|
+
"calendar:drift": "node scripts/calendar-drift.mjs",
|
|
191
|
+
"calendar:drift:test": "node scripts/calendar-drift.mjs --test",
|
|
192
|
+
"constants:check": "node scripts/constants-check.mjs",
|
|
193
|
+
"constants:seed": "node scripts/constants-check.mjs --seed"
|
|
184
194
|
},
|
|
185
195
|
"keywords": [
|
|
186
196
|
"a432",
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"browserOnly": {
|
|
3
|
+
"src/0/3/6/9/1/2/4/8/7/5/1/a432.main.ts": "document is not defined"
|
|
4
|
+
},
|
|
5
|
+
"stateful": {
|
|
6
|
+
"a432.bus.ts:mergedDigit": "returns a different value on a second call with no arguments",
|
|
7
|
+
"a432.consciousness.stream.ts:evolveConsciousnessStream": "returns a different value on a second call with no arguments",
|
|
8
|
+
"a432.consciousness.stream.ts:getConsciousnessStream": "returns a different value on a second call with no arguments",
|
|
9
|
+
"a432.documentation.ts:generateDocumentationIndex": "returns a different value on a second call with no arguments",
|
|
10
|
+
"a432.experience.ui.ts:createA432ExperienceState": "returns a different value on a second call with no arguments",
|
|
11
|
+
"a432.experience.ui.ts:harmonizeA432Experience": "returns a different value on a second call with no arguments",
|
|
12
|
+
"a432.index.ts:exportA432Registry": "returns a different value on a second call with no arguments",
|
|
13
|
+
"a432.index.ts:getA432RegistryStats": "returns a different value on a second call with no arguments",
|
|
14
|
+
"a432.living.ui.ts:getLivingUIStatus": "returns a different value on a second call with no arguments",
|
|
15
|
+
"a432.navigation.ts:getPath": "returns a different value on a second call with no arguments",
|
|
16
|
+
"a432.navigation.ts:nextSuggestion": "returns a different value on a second call with no arguments",
|
|
17
|
+
"a432.os.ts:a432OSState": "returns a different value on a second call with no arguments",
|
|
18
|
+
"a432.os.ts:getA432SystemStatus": "returns a different value on a second call with no arguments",
|
|
19
|
+
"a432.os.ts:getSystemInfo": "returns a different value on a second call with no arguments",
|
|
20
|
+
"a432.registry.ts:exportA432Registry": "returns a different value on a second call with no arguments",
|
|
21
|
+
"a432.registry.ts:getA432RegistryStats": "returns a different value on a second call with no arguments",
|
|
22
|
+
"a432.stream.vortex.ts:getStreamVortexOverlayData": "returns a different value on a second call with no arguments",
|
|
23
|
+
"a432.time.ts:now": "returns a different value on a second call with no arguments"
|
|
24
|
+
},
|
|
25
|
+
"notDigits": {
|
|
26
|
+
"a432.mirror.ts:a432PrimeSquaredBases": "returns numbers outside 0..9, so it is not a digit sequence",
|
|
27
|
+
"a432.pi.ts:piHarmonicStream": "returns numbers outside 0..9, so it is not a digit sequence",
|
|
28
|
+
"a432.shear.electron.ts:a432NestedVortices": "returns numbers outside 0..9, so it is not a digit sequence",
|
|
29
|
+
"a432.vbm.audio.ts:getVBMAudioFrequencies": "returns numbers outside 0..9, so it is not a digit sequence",
|
|
30
|
+
"a432.vbm.visualization.ts:getVBMVisualizationAngles": "returns numbers outside 0..9, so it is not a digit sequence"
|
|
31
|
+
},
|
|
32
|
+
"emptyByDesign": {
|
|
33
|
+
"a432.harmonized.ts:getAllHarmonizationStrategies": "returns an empty array with no arguments",
|
|
34
|
+
"a432.index.ts:getAllA432Modules": "returns an empty array with no arguments",
|
|
35
|
+
"a432.matrix.ts:getTrinityIndices": "returns an empty array with no arguments",
|
|
36
|
+
"a432.navigation.ts:getPath": "returns an empty array with no arguments",
|
|
37
|
+
"a432.registry.ts:getAllA432Modules": "returns an empty array with no arguments",
|
|
38
|
+
"a432.ui.session.ts:getSessionHistory": "returns an empty array with no arguments"
|
|
39
|
+
},
|
|
40
|
+
"triadByDesign": {
|
|
41
|
+
"a432.coil.ts:a432VortexWell": "named for the doubling circuit but contains 3, 6 or 9",
|
|
42
|
+
"a432.trinity.ts:getTrinityAxisFromRodin": "named for the doubling circuit but contains 3, 6 or 9",
|
|
43
|
+
"a432.trinity.ts:getTrinityTriangulationFromRodin": "named for the doubling circuit but contains 3, 6 or 9"
|
|
44
|
+
},
|
|
45
|
+
"sharedValues": {
|
|
46
|
+
"getDoublingSequence = getRodinSequence": "[1,2,4,8,7,5,1]",
|
|
47
|
+
"a432ApertureFlow = a432RodinCoil = getOpenDoublingSequence = rodinOrbit = stringTheoryOrbit": "[1,2,4,8,7,5]",
|
|
48
|
+
"a432DandelionPuff = a432DiamondFacets = generateConsciousness": "[1,2,3,4,5,6,7,8,9]",
|
|
49
|
+
"a432ClosedSystem = a432ConsciousnessMap = a432RodinCoilStream = a432ToroidalMap": "[1,2,4,8,7,5,1,2,4,8,7,5]",
|
|
50
|
+
"a432HeatDissipation = a432Palindrome396 = a432SpiritDispersion": "[3,9,6,6,9,3,3,9,6]",
|
|
51
|
+
"getMetaDescriptions = getRecursiveMetaObservation": "[\"TrinityNavigation: Navigates, observes, and harmonizes the trinity and family streams.\"]",
|
|
52
|
+
"generateCycle = getVBMAudioSequence = getVBMVisualizationSequence": "[0,3,6,9,1,2,4,8,7,5,1]",
|
|
53
|
+
"generateVortex = generateVortexSequence": "[1,2,4,8,7,5,1,2,4]",
|
|
54
|
+
"getTrinityAxis = getTrinityAxisFromRodin = manifestTrinityAxis = stringTheoryAxis": "[3,6,9]",
|
|
55
|
+
"a432MetaVortex = a432UnifiedMatrix": "{\"trinity\":[3,6,9,3,6,9,3,6,9],\"family\":{\"F1\":[1,2,4,8,7,5,1],\"F2\":[3,9,6,3],\"F3\":[9]},\"nine\":[3,9,6,3,9,6,3,9,6],\"casca…",
|
|
56
|
+
"a432ElectronShear = a432Shears": "[1,4,7]",
|
|
57
|
+
"getCurrentNavState = trinityPrev": "{\"step\":1,\"trinity\":3,\"family\":1,\"archetype\":\"Creation\",\"metaphysical\":\"Step 1: Trinity 3 (Creation), Family 1\"}"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The a432 layer, tested by property rather than by example.
|
|
3
|
+
*
|
|
4
|
+
* 198 modules, 35,876 lines, 1,489 exports, and until this file no tests at
|
|
5
|
+
* all. That is why a function named generateVortex could return the counting
|
|
6
|
+
* sequence in THREE separate places and nothing said a word.
|
|
7
|
+
*
|
|
8
|
+
* Writing 198 example-based test files would not have caught it. Nobody writes
|
|
9
|
+
* `expect(generateVortex(9)).toEqual([1,2,4,8,7,5,1,2,4])` for a function they
|
|
10
|
+
* believe already works — and if they did, they would write the assertion from
|
|
11
|
+
* the same wrong belief that produced the bug. Every defect found in this layer
|
|
12
|
+
* was found MECHANICALLY, by running everything and looking for a shape that
|
|
13
|
+
* cannot be right:
|
|
14
|
+
*
|
|
15
|
+
* - output identical to a differently-named function
|
|
16
|
+
* - a "vortex" containing 3, 6 or 9, which doubling provably never reaches
|
|
17
|
+
* - a validator returning false about its own data
|
|
18
|
+
* - a branch nothing ever executes
|
|
19
|
+
*
|
|
20
|
+
* So those are the properties. They run over every module, so a NEW module is
|
|
21
|
+
* covered the day it appears rather than the day somebody remembers it.
|
|
22
|
+
*
|
|
23
|
+
* Exceptions are declared in a432.test.json, seeded from the current state the
|
|
24
|
+
* way the ratchet records its ceilings. The point is not to relitigate what is
|
|
25
|
+
* already here; it is that the next one cannot arrive unnoticed. A declaration
|
|
26
|
+
* that stops being true fails too, so the file cannot rot into a list of
|
|
27
|
+
* excuses for things that no longer exist.
|
|
28
|
+
*
|
|
29
|
+
* npm run test:a432 run the properties
|
|
30
|
+
* npm run test:a432:seed rewrite the declarations from the current state
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs'
|
|
34
|
+
import { dirname, join, relative } from 'node:path'
|
|
35
|
+
import { fileURLToPath } from 'node:url'
|
|
36
|
+
import { pathToFileURL } from 'node:url'
|
|
37
|
+
import { VORTEX_ORBIT, VORTEX_AXIS, digitalRoot, vortexStrokeGateways } from '../../../../../../../../../../index.ts'
|
|
38
|
+
|
|
39
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
40
|
+
/** Walk up to the package root rather than counting ../ eleven times. */
|
|
41
|
+
function packageRoot(from: string): string {
|
|
42
|
+
let d = from
|
|
43
|
+
for (;;) {
|
|
44
|
+
try { statSync(join(d, 'package.json')); return d } catch { /* keep going */ }
|
|
45
|
+
const up = dirname(d)
|
|
46
|
+
if (up === d) throw new Error('no package.json above ' + from)
|
|
47
|
+
d = up
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const ROOT = packageRoot(HERE)
|
|
51
|
+
const DECL = join(HERE, 'a432.test.json')
|
|
52
|
+
const SEED = process.argv.includes('--seed')
|
|
53
|
+
|
|
54
|
+
interface Declarations {
|
|
55
|
+
browserOnly: Record<string, string>
|
|
56
|
+
stateful: Record<string, string>
|
|
57
|
+
notDigits: Record<string, string>
|
|
58
|
+
emptyByDesign: Record<string, string>
|
|
59
|
+
triadByDesign: Record<string, string>
|
|
60
|
+
sharedValues: Record<string, string>
|
|
61
|
+
}
|
|
62
|
+
const EMPTY: Declarations = {
|
|
63
|
+
browserOnly: {}, stateful: {}, notDigits: {}, emptyByDesign: {}, triadByDesign: {}, sharedValues: {},
|
|
64
|
+
}
|
|
65
|
+
let decl: Declarations = EMPTY
|
|
66
|
+
try { decl = { ...EMPTY, ...JSON.parse(readFileSync(DECL, 'utf8')) } } catch { /* seeding */ }
|
|
67
|
+
|
|
68
|
+
let failures = 0
|
|
69
|
+
const problems: string[] = []
|
|
70
|
+
function check(name: string, ok: boolean, detail = ''): void {
|
|
71
|
+
if (ok) return
|
|
72
|
+
failures++
|
|
73
|
+
problems.push(`${name}${detail ? ' — ' + detail : ''}`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ------------------------------------------------------------------ modules
|
|
77
|
+
function a432Files(dir: string, out: string[] = []): string[] {
|
|
78
|
+
for (const n of readdirSync(dir)) {
|
|
79
|
+
const full = join(dir, n)
|
|
80
|
+
if (statSync(full).isDirectory()) a432Files(full, out)
|
|
81
|
+
else if (/^a432\..*\.ts$/.test(n) && !n.endsWith('.test.ts')) out.push(full)
|
|
82
|
+
}
|
|
83
|
+
return out
|
|
84
|
+
}
|
|
85
|
+
const files = a432Files(join(ROOT, 'src')).sort()
|
|
86
|
+
|
|
87
|
+
// ------------------------------------------------------ load every module
|
|
88
|
+
interface Callable { where: string; call: () => unknown }
|
|
89
|
+
const callables: Callable[] = []
|
|
90
|
+
const loaded: string[] = []
|
|
91
|
+
const seedBrowserOnly: Record<string, string> = {}
|
|
92
|
+
|
|
93
|
+
// Imported IN ORDER, deliberately, and this is not an oversight.
|
|
94
|
+
//
|
|
95
|
+
// I parallelised this with Promise.all first, on the reasoning that the modules
|
|
96
|
+
// do not depend on each other's evaluation order. Several of them register
|
|
97
|
+
// themselves at module scope, so the order decides what the registry contains
|
|
98
|
+
// when a later module reads it — and the suite went flaky, failing 3 runs in 6
|
|
99
|
+
// with a different function named each time. It also bought nothing measurable:
|
|
100
|
+
// 198 sequential imports are ~395ms, and the parallel suite still took ~407ms
|
|
101
|
+
// end to end. A flaky gate is worse than a slow one, and this one was not even
|
|
102
|
+
// faster.
|
|
103
|
+
const imported: { file: string; mod: Record<string, unknown> | null; err: Error | null }[] = []
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
try { imported.push({ file, mod: (await import(pathToFileURL(file).href)) as Record<string, unknown>, err: null }) }
|
|
106
|
+
catch (e) { imported.push({ file, mod: null, err: e as Error }) }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const { file, mod: maybeMod, err } of imported) {
|
|
110
|
+
const rel = relative(ROOT, file)
|
|
111
|
+
const short = file.split('/').pop()!
|
|
112
|
+
if (err !== null) {
|
|
113
|
+
const msg = err.message.split('\n')[0]!
|
|
114
|
+
// A module needing `document` is a browser module, not a broken one — but
|
|
115
|
+
// it has to SAY so, so that a genuine load failure cannot hide among them.
|
|
116
|
+
if (/\b(document|window|self|localStorage) is not defined\b/.test(msg)) {
|
|
117
|
+
seedBrowserOnly[rel] = msg
|
|
118
|
+
check(`${short} loads`, rel in decl.browserOnly, `browser-only and undeclared: ${msg}`)
|
|
119
|
+
} else {
|
|
120
|
+
check(`${short} loads`, false, msg)
|
|
121
|
+
}
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
const mod = maybeMod!
|
|
125
|
+
loaded.push(rel)
|
|
126
|
+
const visit = (name: string, fn: unknown, self: unknown): void => {
|
|
127
|
+
if (typeof fn !== 'function' || fn.length !== 0) return
|
|
128
|
+
callables.push({ where: `${short}:${name}`, call: () => (fn as (this: unknown) => unknown).call(self) })
|
|
129
|
+
}
|
|
130
|
+
for (const [k, v] of Object.entries(mod)) {
|
|
131
|
+
visit(k, v, mod)
|
|
132
|
+
if (typeof v === 'function') {
|
|
133
|
+
for (const s of Object.getOwnPropertyNames(v)) {
|
|
134
|
+
if (['length', 'name', 'prototype', 'caller', 'arguments'].includes(s)) continue
|
|
135
|
+
visit(`${k}.${s}`, (v as Record<string, unknown>)[s], v)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Every declared browser-only module must still BE one.
|
|
142
|
+
for (const rel of Object.keys(decl.browserOnly)) {
|
|
143
|
+
check(`declaration browserOnly ${rel}`, !loaded.includes(rel), 'it loads fine now — drop the entry')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --------------------------------------------------------------- run them
|
|
147
|
+
interface Result { where: string; a: string | undefined; b: string | undefined; value: unknown }
|
|
148
|
+
// Two full passes, not two back-to-back calls.
|
|
149
|
+
//
|
|
150
|
+
// Calling f() twice in a row puts both calls in the same millisecond, so a
|
|
151
|
+
// function that reads the clock returns the same value and looks deterministic.
|
|
152
|
+
// That is not a hypothetical: getStreamVortexOverlayData passed the paired-call
|
|
153
|
+
// version most of the time and failed about one run in eight, and the SEED had
|
|
154
|
+
// the same blind spot, so it was never declared either. Sampling noise in both
|
|
155
|
+
// the measurement and the allowlist built from it.
|
|
156
|
+
//
|
|
157
|
+
// Separating each function's two calls by a whole pass over the other 405 gives
|
|
158
|
+
// a real time gap for the same total number of calls — cheaper than sleeping
|
|
159
|
+
// per function, and it decides rather than samples.
|
|
160
|
+
const results: Result[] = []
|
|
161
|
+
const first: { c: Callable; a: string | undefined; value: unknown }[] = []
|
|
162
|
+
for (const c of callables) {
|
|
163
|
+
try { const value = c.call(); first.push({ c, a: JSON.stringify(value), value }) } catch { /* not callable bare */ }
|
|
164
|
+
}
|
|
165
|
+
for (const { c, a, value } of first) {
|
|
166
|
+
let b: string | undefined
|
|
167
|
+
try { b = JSON.stringify(c.call()) } catch { /* second call threw; a !== b catches it */ }
|
|
168
|
+
results.push({ where: c.where, a, b, value })
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ---------------------------------------------------- P1 determinism
|
|
172
|
+
// This layer bans Math.random and fork(), so a nullary export that answers
|
|
173
|
+
// differently twice is either holding mutable state or reading the clock.
|
|
174
|
+
// Both are legitimate for some things; neither may be silent.
|
|
175
|
+
const seedStateful: Record<string, string> = {}
|
|
176
|
+
for (const r of results) {
|
|
177
|
+
if (r.a === r.b) continue
|
|
178
|
+
seedStateful[r.where] = 'returns a different value on a second call with no arguments'
|
|
179
|
+
check(`${r.where} is deterministic`, r.where in decl.stateful, 'undeclared state or clock dependence')
|
|
180
|
+
}
|
|
181
|
+
// No reverse check on `stateful`, unlike the other declarations. Calling a
|
|
182
|
+
// function twice can only ever DETECT non-determinism, never establish
|
|
183
|
+
// determinism: one that reads the clock returns the same value whenever both
|
|
184
|
+
// calls land in the same millisecond, which is most of the time. Requiring a
|
|
185
|
+
// declared entry to keep failing would make the suite fail at random, which is
|
|
186
|
+
// exactly what it did before this note existed. Undeclared non-determinism
|
|
187
|
+
// still fails, and that is the direction that finds things.
|
|
188
|
+
|
|
189
|
+
// ------------------------------------------- P2 digit sequences are digits
|
|
190
|
+
// A digit in this system is 0..9. An array of numbers that leaves that range
|
|
191
|
+
// is an angle or a frequency, which is fine, but must say so.
|
|
192
|
+
const seedNotDigits: Record<string, string> = {}
|
|
193
|
+
const isNumArray = (v: unknown): v is number[] =>
|
|
194
|
+
Array.isArray(v) && v.length > 0 && v.every((x) => typeof x === 'number')
|
|
195
|
+
for (const r of results) {
|
|
196
|
+
if (!isNumArray(r.value)) continue
|
|
197
|
+
const bad = r.value.some((x) => !Number.isInteger(x) || x < 0 || x > 9)
|
|
198
|
+
if (!bad) continue
|
|
199
|
+
seedNotDigits[r.where] = 'returns numbers outside 0..9, so it is not a digit sequence'
|
|
200
|
+
check(`${r.where} yields digits`, r.where in decl.notDigits, 'outside 0..9 and undeclared')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ------------------------------------------------ P3 empty is a decision
|
|
204
|
+
// getTrinityAxisFromRodin returned [] because it filtered the doubling circuit
|
|
205
|
+
// for 3, 6 and 9 — which doubling_avoids_the_triad proves are never there. An
|
|
206
|
+
// empty array is how a function that cannot work looks from outside.
|
|
207
|
+
const seedEmpty: Record<string, string> = {}
|
|
208
|
+
for (const r of results) {
|
|
209
|
+
if (!Array.isArray(r.value) || r.value.length !== 0) continue
|
|
210
|
+
seedEmpty[r.where] = 'returns an empty array with no arguments'
|
|
211
|
+
check(`${r.where} returns something`, r.where in decl.emptyByDesign, 'empty and undeclared')
|
|
212
|
+
}
|
|
213
|
+
for (const w of Object.keys(decl.emptyByDesign)) {
|
|
214
|
+
const r = results.find((x) => x.where === w)
|
|
215
|
+
if (r) check(`declaration emptyByDesign ${w}`, Array.isArray(r.value) && r.value.length === 0, 'it returns data now — drop the entry')
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---------------------------------- P4 the doubling circuit is the doubling circuit
|
|
219
|
+
// The property the three broken copies of generateVortex all failed.
|
|
220
|
+
const DOUBLING_NAME = /vortex|rodin|orbit|doubling/i
|
|
221
|
+
const TRIAD: readonly number[] = VORTEX_AXIS
|
|
222
|
+
const seedTriad: Record<string, string> = {}
|
|
223
|
+
for (const r of results) {
|
|
224
|
+
if (!DOUBLING_NAME.test(r.where)) continue
|
|
225
|
+
if (!isNumArray(r.value)) continue
|
|
226
|
+
if (r.value.some((x) => x > 9)) continue // an angle series, covered by P2
|
|
227
|
+
const hasTriad = r.value.some((d) => TRIAD.includes(d))
|
|
228
|
+
if (hasTriad) {
|
|
229
|
+
seedTriad[r.where] = 'named for the doubling circuit but contains 3, 6 or 9'
|
|
230
|
+
check(`${r.where} avoids the triad`, r.where in decl.triadByDesign, 'doubling cannot reach 3, 6 or 9')
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
233
|
+
// No triad: then every step must actually be a doubling step, and every
|
|
234
|
+
// member must be in the kernel's orbit. This is what makes 1..9 impossible.
|
|
235
|
+
const inOrbit = r.value.every((d) => VORTEX_ORBIT.includes(d))
|
|
236
|
+
check(`${r.where} stays in the orbit`, inOrbit, JSON.stringify(r.value))
|
|
237
|
+
if (inOrbit && r.value.length > 1) {
|
|
238
|
+
const steps = r.value.every((d, i) => i === 0 || digitalRoot(r.value[i - 1]! * 2) === d)
|
|
239
|
+
check(`${r.where} advances by doubling`, steps, JSON.stringify(r.value))
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ------------------------------------------------- P5 no silent duplicates
|
|
244
|
+
// Two differently-named nullary functions returning the same value means at
|
|
245
|
+
// least one is not computing what its name says. That is how generateVortex
|
|
246
|
+
// was caught: it was byte-identical to generateConsciousness.
|
|
247
|
+
const byValue = new Map<string, string[]>()
|
|
248
|
+
for (const r of results) {
|
|
249
|
+
if (r.a === undefined || r.a.length < 5 || r.a.length > 400) continue
|
|
250
|
+
if (!byValue.has(r.a)) byValue.set(r.a, [])
|
|
251
|
+
byValue.get(r.a)!.push(r.where)
|
|
252
|
+
}
|
|
253
|
+
const seedShared: Record<string, string> = {}
|
|
254
|
+
for (const [value, who] of byValue) {
|
|
255
|
+
const distinct = new Set(who.map((w) => w.split(':')[1]!.split('.').pop()!))
|
|
256
|
+
if (distinct.size < 2) continue // re-exports of one function are not duplicates
|
|
257
|
+
const key = [...distinct].sort().join(' = ')
|
|
258
|
+
seedShared[key] = value.length > 120 ? value.slice(0, 120) + '…' : value
|
|
259
|
+
check(`no undeclared duplicate: ${key}`, key in decl.sharedValues, value.slice(0, 60))
|
|
260
|
+
}
|
|
261
|
+
for (const k of Object.keys(decl.sharedValues)) {
|
|
262
|
+
check(`declaration sharedValues ${k}`, k in seedShared, 'these no longer agree — drop the entry')
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ------------------------------------------------------------- regressions
|
|
266
|
+
// Named, so the three specific defects this layer shipped cannot come back.
|
|
267
|
+
/** Only the members these regressions touch — `any` would defeat the point. */
|
|
268
|
+
type Seq = { A432Sequence: { generateVortex(n: number): number[]; generateVortexSequence(n: number): number[]; generateConsciousness(n: number): number[] } }
|
|
269
|
+
type Frac = {
|
|
270
|
+
validateAllA432Fractions(): boolean
|
|
271
|
+
validateA432Fraction(f: { numerator: number; denominator: number }): boolean
|
|
272
|
+
}
|
|
273
|
+
type Trin = { getTrinityAxisFromRodin(seq?: number[]): number[]; getTrinityTriangulationFromRodin(seq?: number[]): number[] }
|
|
274
|
+
const load = async <T>(name: string): Promise<T> => (await import(pathToFileURL(join(HERE, name)).href)) as T
|
|
275
|
+
const utils = await load<Seq>('a432.utils.ts')
|
|
276
|
+
const simple = await load<Seq>('a432.simple.ts')
|
|
277
|
+
const resolved = await load<Frac>('a432.resolved.ts')
|
|
278
|
+
const trinity = await load<Trin>('a432.trinity.ts')
|
|
279
|
+
|
|
280
|
+
const orbit = VORTEX_ORBIT.join()
|
|
281
|
+
check('utils.generateVortex is the orbit', utils.A432Sequence.generateVortex(VORTEX_ORBIT.length).join() === orbit)
|
|
282
|
+
check('simple.generateVortexSequence is the orbit', simple.A432Sequence.generateVortexSequence(VORTEX_ORBIT.length).join() === orbit)
|
|
283
|
+
check(
|
|
284
|
+
'generateVortex differs from generateConsciousness',
|
|
285
|
+
utils.A432Sequence.generateVortex(9).join() !== utils.A432Sequence.generateConsciousness(9).join(),
|
|
286
|
+
)
|
|
287
|
+
check('every declared A432 fraction validates', resolved.validateAllA432Fractions() === true)
|
|
288
|
+
check('the fraction validator still rejects a decimal', resolved.validateA432Fraction({ numerator: 4 / 5, denominator: 1 }) === false)
|
|
289
|
+
check('the fraction validator still rejects 2/4', resolved.validateA432Fraction({ numerator: 2, denominator: 4 }) === false)
|
|
290
|
+
// ---------------------------------------------------------------- gateways
|
|
291
|
+
// A gateway is not a label. The vortex stroke is a closed ten-step tour
|
|
292
|
+
// 1\2\4\8/7/5/3\6\9/0\1, each step drawn `\` or `/`, and a gateway is a digit
|
|
293
|
+
// whose outgoing stroke REVERSES from the one that arrived at it. There are
|
|
294
|
+
// exactly four. Re-derived here from the steps rather than compared against a
|
|
295
|
+
// stored list, so a change to the stroke has to survive the derivation.
|
|
296
|
+
const stroke = vortexStrokeGateways() as {
|
|
297
|
+
gateways: number[]; steps: { from: number; to: number; stroke: string }[]; tour: number[]
|
|
298
|
+
}
|
|
299
|
+
const st = stroke.steps
|
|
300
|
+
const derivedGateways = st
|
|
301
|
+
.filter((step, i) => step.stroke !== st[(i - 1 + st.length) % st.length]!.stroke)
|
|
302
|
+
.map((step) => step.from)
|
|
303
|
+
check('gateways are the polarity reversals of the stroke', derivedGateways.join() === stroke.gateways.join(),
|
|
304
|
+
`derived ${JSON.stringify(derivedGateways)} vs ${JSON.stringify(stroke.gateways)}`)
|
|
305
|
+
check('there are exactly four gateways', stroke.gateways.length === 4, JSON.stringify(stroke.gateways))
|
|
306
|
+
check('the tour is closed', st[st.length - 1]!.to === st[0]!.from)
|
|
307
|
+
check('the tour visits every digit once', new Set(stroke.tour).size === 10 && stroke.tour.length === 10)
|
|
308
|
+
check('the tour opens with the doubling orbit', stroke.tour.slice(0, VORTEX_ORBIT.length).join() === VORTEX_ORBIT.join())
|
|
309
|
+
// 6 is on the axis and is NOT a gateway: being on the axis is not what makes
|
|
310
|
+
// one, which is the whole reason they have to be derived rather than listed.
|
|
311
|
+
check('6 is on the axis but is not a gateway', VORTEX_AXIS.includes(6) && !stroke.gateways.includes(6))
|
|
312
|
+
|
|
313
|
+
check('the trinity axis derived from Rodin is the triad', trinity.getTrinityAxisFromRodin().join() === VORTEX_AXIS.join(),
|
|
314
|
+
JSON.stringify(trinity.getTrinityAxisFromRodin()))
|
|
315
|
+
check('trinity triangulation uses its argument', trinity.getTrinityTriangulationFromRodin([1, 2, 4]).length !== trinity.getTrinityTriangulationFromRodin([3, 6, 9]).length,
|
|
316
|
+
'same answer for different inputs: the parameter is ignored')
|
|
317
|
+
|
|
318
|
+
// -------------------------------------------------------------------- seed
|
|
319
|
+
if (SEED) {
|
|
320
|
+
const seeded: Declarations = {
|
|
321
|
+
browserOnly: seedBrowserOnly,
|
|
322
|
+
stateful: seedStateful,
|
|
323
|
+
notDigits: seedNotDigits,
|
|
324
|
+
emptyByDesign: seedEmpty,
|
|
325
|
+
triadByDesign: seedTriad,
|
|
326
|
+
sharedValues: seedShared,
|
|
327
|
+
}
|
|
328
|
+
writeFileSync(DECL, JSON.stringify(seeded, null, 2) + '\n')
|
|
329
|
+
const n = Object.values(seeded).reduce((s, o) => s + Object.keys(o).length, 0)
|
|
330
|
+
console.log(`a432 test:seed wrote ${n} declarations to ${relative(ROOT, DECL)}`)
|
|
331
|
+
process.exit(0)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log(
|
|
335
|
+
`a432 properties — ${files.length} modules, ${loaded.length} loaded, ` +
|
|
336
|
+
`${results.length} nullary exports called`,
|
|
337
|
+
)
|
|
338
|
+
for (const p of problems) console.error(` ✗ ${p}`)
|
|
339
|
+
if (failures > 0) {
|
|
340
|
+
console.error(`a432 tests FAIL — ${failures} problem(s)`)
|
|
341
|
+
process.exit(1)
|
|
342
|
+
}
|
|
343
|
+
console.log('a432 tests ok — every property holds, and every declaration is still true')
|
|
344
|
+
process.exit(0)
|
|
@@ -18,6 +18,7 @@ import { PI, abs, cos, floor, min, round, sin, sqrt } from './a432.algebra.ts'
|
|
|
18
18
|
import { digitAngleToCMYK, cmykToCss, type CMYK } from './a432.cmyk.ts';
|
|
19
19
|
import { A432_FREQUENCY } from './a432.core.ts';
|
|
20
20
|
import { TRINITY_AXIS, RODIN_SEQUENCE } from './a432.math.ts';
|
|
21
|
+
import { throughVoid } from '../../../../../../../../../../index.ts';
|
|
21
22
|
export { TRINITY_AXIS, RODIN_SEQUENCE };
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -34,13 +35,37 @@ export function trinityFieldFold(angleA: number, angleB: number): number {
|
|
|
34
35
|
return trinityFieldState(mergedAngle);
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
const isTriad = (n: number): boolean => n === 3 || n === 6 || n === 9;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The trinity axis reached from a Rodin sequence.
|
|
42
|
+
*
|
|
43
|
+
* This filtered rodinSeq for 3, 6 and 9 directly, and therefore always returned
|
|
44
|
+
* an empty array — the sealed theorem doubling_avoids_the_triad proves that the
|
|
45
|
+
* doubling circuit never contains them, because gcd(2,9) = 1 makes every power
|
|
46
|
+
* of two a unit mod 9. Filtering for something a proof says is not there is not
|
|
47
|
+
* a search that sometimes fails; it is one that cannot succeed.
|
|
48
|
+
*
|
|
49
|
+
* The same theorem says how the triad IS reached: by reflection through the
|
|
50
|
+
* void, which carries 1, 4 and 7 onto 9, 6 and 3. So the sequence is reflected
|
|
51
|
+
* first, and the triad members of the reflection are the axis.
|
|
52
|
+
*/
|
|
37
53
|
export function getTrinityAxisFromRodin(rodinSeq: number[] = RODIN_SEQUENCE): number[] {
|
|
38
|
-
|
|
54
|
+
const reflected = rodinSeq.map(throughVoid).filter(isTriad);
|
|
55
|
+
return Array.from(new Set(reflected)).sort((a, b) => a - b);
|
|
39
56
|
}
|
|
40
57
|
|
|
58
|
+
/**
|
|
59
|
+
* The trinity triangulation reached from a Rodin sequence.
|
|
60
|
+
*
|
|
61
|
+
* This declared a rodinSeq parameter and then ignored it, testing membership
|
|
62
|
+
* against a hardcoded cycle instead, so every input produced the same answer.
|
|
63
|
+
* It now answers about the sequence it was actually given, by the same
|
|
64
|
+
* reflection: 3, 9, 6 in triangulation order, keeping those the input can reach.
|
|
65
|
+
*/
|
|
41
66
|
export function getTrinityTriangulationFromRodin(rodinSeq: number[] = RODIN_SEQUENCE): number[] {
|
|
42
|
-
const
|
|
43
|
-
return [3, 9, 6].filter(n =>
|
|
67
|
+
const reachable = new Set(rodinSeq.map(throughVoid).filter(isTriad));
|
|
68
|
+
return [3, 9, 6].filter(n => reachable.has(n));
|
|
44
69
|
}
|
|
45
70
|
|
|
46
71
|
// Use canonical constants from a432.math.ts
|
package/src/vbm-math.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { pathToFileURL } from 'node:url'
|
|
9
9
|
import { abs, pow } from './0/algebra.ts'
|
|
10
|
+
import { VORTEX_ORBIT, VORTEX_AXIS } from './0/index.ts'
|
|
10
11
|
import { legacyDigitalRoot } from './0/3/6/9/1/2/4/8/7/5/1/a432.roots.ts'
|
|
11
12
|
|
|
12
13
|
export class VortexMath {
|
|
@@ -41,10 +42,14 @@ export class VortexMath {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
*
|
|
45
|
+
* The core Mobius circuit — the kernel's VORTEX_ORBIT, not a second copy.
|
|
46
|
+
*
|
|
47
|
+
* This returned a literal, so it was one of 91 places in the tree holding
|
|
48
|
+
* its own copy of a constant the kernel already exports. Bound rather than
|
|
49
|
+
* retyped, it cannot drift from the definition.
|
|
45
50
|
*/
|
|
46
51
|
static getMobiusCircuit(): number[] {
|
|
47
|
-
return [
|
|
52
|
+
return [...VORTEX_ORBIT];
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/**
|
|
@@ -52,7 +57,7 @@ export class VortexMath {
|
|
|
52
57
|
* These numbers form the higher dimensional control axis in VBM
|
|
53
58
|
*/
|
|
54
59
|
static getSpiritNumbers(): number[] {
|
|
55
|
-
return [
|
|
60
|
+
return [...VORTEX_AXIS];
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
/**
|