@soulcraft/cor 3.0.16 → 3.0.17
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/dist/utils/NativeMetadataIndex.d.ts +10 -0
- package/dist/utils/NativeMetadataIndex.js +111 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -563,6 +563,16 @@ export declare class MetadataIndexManager implements MetadataIndexProvider {
|
|
|
563
563
|
* same rebuild). Skipped at/above the u32 saturation point of
|
|
564
564
|
* `getTotalEntityCount` (a saturated count would make equality lie).
|
|
565
565
|
*/
|
|
566
|
+
/**
|
|
567
|
+
* The verified-unpostable allowance for posted-vs-canonical floors: how
|
|
568
|
+
* many canonical noun rows the last COMPLETED rebuild PROVED carry no
|
|
569
|
+
* readable metadata (each verified by direct read at rebuild time, each
|
|
570
|
+
* RE-VERIFIED here so an id that has since gained metadata stops
|
|
571
|
+
* counting). Beyond {@link UNPOSTABLE_ID_CAP} the stamp is count-only
|
|
572
|
+
* and the allowance carries a bounded, logged staleness caveat.
|
|
573
|
+
* Advisory: any fault reads as 0 (may over-heal, never under-detects).
|
|
574
|
+
*/
|
|
575
|
+
private verifiedUnpostableAllowance;
|
|
566
576
|
private detectDerivedStrand;
|
|
567
577
|
/**
|
|
568
578
|
* **#72 Phase C.** Build the shared mmap `BinaryIdMapper` from the
|
|
@@ -73,6 +73,20 @@ const LSM_MANIFEST_VERSION = 2;
|
|
|
73
73
|
* field registry already depends on.
|
|
74
74
|
*/
|
|
75
75
|
const LSM_MANIFEST_KEY = '__metadata_lsm_manifest__';
|
|
76
|
+
/**
|
|
77
|
+
* Durable stamp of the entities a COMPLETED rebuild verified as
|
|
78
|
+
* unpostable (canonical rows with NO readable metadata — verified by
|
|
79
|
+
* direct read, not invented postings). The strand detector's
|
|
80
|
+
* posted-count floor accounts for them: without this, a fixed cohort of
|
|
81
|
+
* metadata-less canonical rows makes EVERY boot read as stranded and
|
|
82
|
+
* re-triggers a pointless full heal forever (memory-vm's constant-52,
|
|
83
|
+
* CORTEX-RESTART-STRAND 2026-07-13 — four consecutive boots).
|
|
84
|
+
*/
|
|
85
|
+
const UNPOSTABLE_STAMP_KEY = '__metadata_verified_unpostable__';
|
|
86
|
+
/** Above this many ids the stamp stores count-only (the boot-time
|
|
87
|
+
* re-verify would stop being cheap); detection then carries a bounded
|
|
88
|
+
* staleness caveat, logged when it engages. */
|
|
89
|
+
const UNPOSTABLE_ID_CAP = 1000;
|
|
76
90
|
/**
|
|
77
91
|
* **LSM durability (step 5).** Lower clamp (32 MiB) for the bounded-memtable
|
|
78
92
|
* size-flush trigger. Even when the dynamic 'metadata' budget is tiny, the
|
|
@@ -1275,6 +1289,41 @@ export class MetadataIndexManager {
|
|
|
1275
1289
|
* same rebuild). Skipped at/above the u32 saturation point of
|
|
1276
1290
|
* `getTotalEntityCount` (a saturated count would make equality lie).
|
|
1277
1291
|
*/
|
|
1292
|
+
/**
|
|
1293
|
+
* The verified-unpostable allowance for posted-vs-canonical floors: how
|
|
1294
|
+
* many canonical noun rows the last COMPLETED rebuild PROVED carry no
|
|
1295
|
+
* readable metadata (each verified by direct read at rebuild time, each
|
|
1296
|
+
* RE-VERIFIED here so an id that has since gained metadata stops
|
|
1297
|
+
* counting). Beyond {@link UNPOSTABLE_ID_CAP} the stamp is count-only
|
|
1298
|
+
* and the allowance carries a bounded, logged staleness caveat.
|
|
1299
|
+
* Advisory: any fault reads as 0 (may over-heal, never under-detects).
|
|
1300
|
+
*/
|
|
1301
|
+
async verifiedUnpostableAllowance() {
|
|
1302
|
+
try {
|
|
1303
|
+
const stamp = (await this.storage.getMetadata(UNPOSTABLE_STAMP_KEY));
|
|
1304
|
+
if (!stamp || typeof stamp.nounCount !== 'number' || stamp.nounCount <= 0)
|
|
1305
|
+
return 0;
|
|
1306
|
+
if (Array.isArray(stamp.nounIds) && stamp.nounIds.length === stamp.nounCount) {
|
|
1307
|
+
const getMeta = this.storage.getNounMetadata;
|
|
1308
|
+
if (typeof getMeta !== 'function')
|
|
1309
|
+
return stamp.nounCount;
|
|
1310
|
+
let stillUnpostable = 0;
|
|
1311
|
+
for (const id of stamp.nounIds) {
|
|
1312
|
+
const md = await getMeta.call(this.storage, id).catch(() => undefined);
|
|
1313
|
+
if (md == null)
|
|
1314
|
+
stillUnpostable++;
|
|
1315
|
+
}
|
|
1316
|
+
return stillUnpostable;
|
|
1317
|
+
}
|
|
1318
|
+
prodLog.warn(`[NativeMetadataIndex] unpostable stamp is count-only ` +
|
|
1319
|
+
`(${stamp.nounCount} > id cap) — the posted-count floor carries its ` +
|
|
1320
|
+
`staleness until the next completed rebuild refreshes it.`);
|
|
1321
|
+
return stamp.nounCount;
|
|
1322
|
+
}
|
|
1323
|
+
catch {
|
|
1324
|
+
return 0;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1278
1327
|
async detectDerivedStrand() {
|
|
1279
1328
|
if (!this.lsmEngine || !this.lsmMetaDir)
|
|
1280
1329
|
return;
|
|
@@ -1364,13 +1413,31 @@ export class MetadataIndexManager {
|
|
|
1364
1413
|
// smaller than the (1–2 entity) system skew is masked here and
|
|
1365
1414
|
// falls to the archive census above; the exact invariant (a
|
|
1366
1415
|
// persisted posted-count stamp) is CORTEX-BILLION-SCALE-SPINE work.
|
|
1367
|
-
|
|
1416
|
+
//
|
|
1417
|
+
// UNPOSTABLE ALLOWANCE (the constant-52, CORTEX-RESTART-STRAND):
|
|
1418
|
+
// canonical rows with NO readable metadata cannot carry a
|
|
1419
|
+
// posting, and a completed rebuild PROVES which those are (each
|
|
1420
|
+
// verified by direct read) and stamps them. Without the
|
|
1421
|
+
// allowance the same cohort re-flags every boot and re-triggers
|
|
1422
|
+
// a pointless full heal forever. The stamp's ids are RE-VERIFIED
|
|
1423
|
+
// here (an id that has since gained metadata stops counting), so
|
|
1424
|
+
// the floor stays exact; beyond UNPOSTABLE_ID_CAP the stamp is
|
|
1425
|
+
// count-only and the allowance carries a bounded, logged
|
|
1426
|
+
// staleness caveat.
|
|
1427
|
+
const unpostable = await this.verifiedUnpostableAllowance();
|
|
1428
|
+
if (posted + unpostable < canonical) {
|
|
1368
1429
|
this.strandDetected = true;
|
|
1369
1430
|
prodLog.error(`[NativeMetadataIndex] STRAND: canonical holds ${canonical} ` +
|
|
1370
|
-
`entities but the index has postings for only ${posted}
|
|
1371
|
-
|
|
1372
|
-
`
|
|
1373
|
-
`
|
|
1431
|
+
`entities but the index has postings for only ${posted}` +
|
|
1432
|
+
(unpostable > 0 ? ` (+${unpostable} verified-unpostable allowance)` : '') +
|
|
1433
|
+
` — ${canonical - posted - unpostable}+ entities are durable in ` +
|
|
1434
|
+
`canonical but UNQUERYABLE (lost postings). Reporting not-ready ` +
|
|
1435
|
+
`so the cold-open gate rebuilds from canonical.`);
|
|
1436
|
+
}
|
|
1437
|
+
else if (unpostable > 0 && posted < canonical) {
|
|
1438
|
+
prodLog.info(`[NativeMetadataIndex] posted ${posted} < canonical ${canonical} is ` +
|
|
1439
|
+
`FULLY explained by ${unpostable} verified metadata-less canonical ` +
|
|
1440
|
+
`entities (stamped by the last completed rebuild) — no strand, no heal.`);
|
|
1374
1441
|
}
|
|
1375
1442
|
}
|
|
1376
1443
|
}
|
|
@@ -2401,15 +2468,23 @@ export class MetadataIndexManager {
|
|
|
2401
2468
|
for (const t of Object.values(NounType)) {
|
|
2402
2469
|
posted += this.native.getEntityCountByType(t);
|
|
2403
2470
|
}
|
|
2471
|
+
// Same verified-unpostable allowance as the strand detector — a
|
|
2472
|
+
// metadata-less canonical row (proven by the last completed
|
|
2473
|
+
// rebuild) cannot carry a posting and must not read as damage
|
|
2474
|
+
// here either, or brainy's repairIndex loops the same heal the
|
|
2475
|
+
// detector fix just broke.
|
|
2476
|
+
const unpostable = await this.verifiedUnpostableAllowance();
|
|
2477
|
+
const floor = posted + unpostable;
|
|
2404
2478
|
return {
|
|
2405
2479
|
name: 'posted-count-floor',
|
|
2406
|
-
holds:
|
|
2407
|
-
detail:
|
|
2408
|
-
? `posted ${posted} ≥ canonical ${canonical}`
|
|
2480
|
+
holds: floor >= canonical,
|
|
2481
|
+
detail: floor >= canonical
|
|
2482
|
+
? `posted ${posted}${unpostable > 0 ? ` (+${unpostable} verified-unpostable)` : ''} ≥ canonical ${canonical}`
|
|
2409
2483
|
: `canonical holds ${canonical} entities but only ${posted} are ` +
|
|
2410
|
-
`posted
|
|
2484
|
+
`posted (${unpostable} verified-unpostable allowed) — ` +
|
|
2485
|
+
`${canonical - floor} durable-but-unqueryable`,
|
|
2411
2486
|
expected: canonical,
|
|
2412
|
-
actual:
|
|
2487
|
+
actual: floor,
|
|
2413
2488
|
heal: 'rebuild',
|
|
2414
2489
|
};
|
|
2415
2490
|
},
|
|
@@ -2904,11 +2979,11 @@ export class MetadataIndexManager {
|
|
|
2904
2979
|
'function';
|
|
2905
2980
|
if (bulkCapable)
|
|
2906
2981
|
this.lsmEngine.setBulkLoad(true);
|
|
2907
|
-
let
|
|
2908
|
-
let
|
|
2982
|
+
let nounResult;
|
|
2983
|
+
let verbResult;
|
|
2909
2984
|
try {
|
|
2910
|
-
|
|
2911
|
-
|
|
2985
|
+
nounResult = await this.rebuildEntityMetadata('noun');
|
|
2986
|
+
verbResult = await this.rebuildEntityMetadata('verb');
|
|
2912
2987
|
}
|
|
2913
2988
|
finally {
|
|
2914
2989
|
// Leaving bulk mode performs the walk's single durability point
|
|
@@ -2922,7 +2997,22 @@ export class MetadataIndexManager {
|
|
|
2922
2997
|
this.isRebuilding = false;
|
|
2923
2998
|
await this.flushRebuildDirty();
|
|
2924
2999
|
await this.flush();
|
|
2925
|
-
|
|
3000
|
+
// Persist what this COMPLETED walk PROVED unpostable (each id
|
|
3001
|
+
// verified by direct canonical read) so the next boot's strand
|
|
3002
|
+
// detector uses the honest floor instead of re-flagging the same
|
|
3003
|
+
// cohort forever. Refreshed on every completed rebuild; an empty
|
|
3004
|
+
// set clears the stamp.
|
|
3005
|
+
await this.storage.saveMetadata(UNPOSTABLE_STAMP_KEY, {
|
|
3006
|
+
nounCount: nounResult.noMetadata,
|
|
3007
|
+
nounIds: nounResult.noMetadataIds,
|
|
3008
|
+
verbCount: verbResult.noMetadata,
|
|
3009
|
+
verifiedAt: Date.now(),
|
|
3010
|
+
});
|
|
3011
|
+
prodLog.info(`Metadata index rebuild completed! Processed ${nounResult.processed} nouns and ` +
|
|
3012
|
+
`${verbResult.processed} verbs` +
|
|
3013
|
+
(nounResult.noMetadata > 0
|
|
3014
|
+
? ` (${nounResult.noMetadata} canonical noun(s) verified metadata-less — stamped for the strand detector)`
|
|
3015
|
+
: ''));
|
|
2926
3016
|
// A completed rebuild re-posted every canonical entity into a freshly
|
|
2927
3017
|
// re-opened engine (reopenLsmEngineFresh wiped the stale shard set) —
|
|
2928
3018
|
// any cold-open strand verdict is now satisfied.
|
|
@@ -2950,6 +3040,7 @@ export class MetadataIndexManager {
|
|
|
2950
3040
|
let processed = 0;
|
|
2951
3041
|
let sinceFlush = 0;
|
|
2952
3042
|
let noMetadata = 0;
|
|
3043
|
+
const noMetadataIds = [];
|
|
2953
3044
|
// Heal observability (CORTEX-RESTART-STRAND, memory's ask): a rebuild
|
|
2954
3045
|
// must never be silent for minutes — the operator cannot distinguish
|
|
2955
3046
|
// converging from stuck. Every 500 posted entities logs progress with
|
|
@@ -3000,6 +3091,8 @@ export class MetadataIndexManager {
|
|
|
3000
3091
|
}
|
|
3001
3092
|
else {
|
|
3002
3093
|
noMetadata++;
|
|
3094
|
+
if (noMetadataIds.length < UNPOSTABLE_ID_CAP)
|
|
3095
|
+
noMetadataIds.push(it.id);
|
|
3003
3096
|
}
|
|
3004
3097
|
}
|
|
3005
3098
|
hasMore = page.hasMore;
|
|
@@ -3013,9 +3106,10 @@ export class MetadataIndexManager {
|
|
|
3013
3106
|
prodLog.warn(`[NativeMetadataIndex] rebuild(${kind}): ${noMetadata} enumerated ` +
|
|
3014
3107
|
`entit${noMetadata === 1 ? 'y' : 'ies'} carried no canonical ` +
|
|
3015
3108
|
`metadata (verified by direct read) and were not indexed — ` +
|
|
3016
|
-
`expected only for metadata-less legacy records
|
|
3109
|
+
`expected only for metadata-less legacy records. First ids: ` +
|
|
3110
|
+
noMetadataIds.slice(0, 10).join(', '));
|
|
3017
3111
|
}
|
|
3018
|
-
return processed;
|
|
3112
|
+
return { processed, noMetadata, noMetadataIds };
|
|
3019
3113
|
}
|
|
3020
3114
|
/**
|
|
3021
3115
|
* Rebuild-path verify-read for a single entity's canonical metadata.
|
package/dist/version.d.ts
CHANGED
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
* `check:brainy`-style drift between the two fails the release.
|
|
12
12
|
*/
|
|
13
13
|
/** The @soulcraft/cor version this build was cut from. */
|
|
14
|
-
export declare const COR_VERSION = "3.0.
|
|
14
|
+
export declare const COR_VERSION = "3.0.17";
|
|
15
15
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/cor",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.17",
|
|
4
4
|
"description": "Native Rust acceleration for Brainy — SIMD distance, vector quantization, zero-copy mmap, native embeddings. Free tier for storage, Pro license for compute acceleration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|