@gamaze/hicortex 0.21.0 → 0.22.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/assets/dashboard.html +282 -27
- package/dist/calibration.d.ts +77 -12
- package/dist/calibration.js +82 -15
- package/dist/capture-health.d.ts +42 -5
- package/dist/capture-health.js +57 -7
- package/dist/capture-pause.d.ts +7 -4
- package/dist/capture-pause.js +7 -4
- package/dist/consolidate.d.ts +19 -0
- package/dist/consolidate.js +125 -51
- package/dist/dashboard.d.ts +41 -16
- package/dist/dashboard.js +110 -41
- package/dist/db.js +16 -0
- package/dist/dedup.js +4 -1
- package/dist/eval/decay-eval.d.ts +6 -5
- package/dist/eval/decay-eval.js +10 -48
- package/dist/eval/eval-clock.d.ts +32 -0
- package/dist/eval/eval-clock.js +47 -0
- package/dist/eval/graph-eval.d.ts +15 -2
- package/dist/eval/graph-eval.js +51 -5
- package/dist/eval/planted-eval.d.ts +4 -0
- package/dist/eval/planted-eval.js +27 -2
- package/dist/eval/planted-harness.d.ts +7 -0
- package/dist/eval/planted-harness.js +2 -0
- package/dist/eval/ranking-battery.d.ts +49 -2
- package/dist/eval/ranking-battery.js +110 -2
- package/dist/eval/ranking-eval.d.ts +26 -6
- package/dist/eval/ranking-eval.js +197 -34
- package/dist/eval/ranking-fixtures.d.ts +46 -6
- package/dist/eval/ranking-fixtures.js +268 -9
- package/dist/eval/recall-sweep.d.ts +7 -2
- package/dist/eval/recall-sweep.js +42 -13
- package/dist/eval/relevance-eval.d.ts +115 -1
- package/dist/eval/relevance-eval.js +318 -32
- package/dist/eval/run-eval.d.ts +7 -4
- package/dist/eval/run-eval.js +37 -10
- package/dist/graph.d.ts +1 -2
- package/dist/graph.js +3 -4
- package/dist/mcp-server.js +3 -2
- package/dist/nofit.d.ts +2 -1
- package/dist/nofit.js +2 -1
- package/dist/recall-index.d.ts +3 -2
- package/dist/recall-index.js +3 -2
- package/dist/retrieval.d.ts +43 -18
- package/dist/retrieval.js +148 -86
- package/dist/status.js +18 -0
- package/dist/storage.d.ts +2 -1
- package/dist/storage.js +6 -1
- package/dist/types.d.ts +25 -4
- package/package.json +1 -1
- package/server.json +4 -4
package/assets/dashboard.html
CHANGED
|
@@ -15,17 +15,22 @@
|
|
|
15
15
|
PORTED FROM mockups/409-r6-flight.html (the design of record — its banner
|
|
16
16
|
documents every behavior; the engine below is that page with the seeded
|
|
17
17
|
data layer replaced by fetches). Live-data substitutions:
|
|
18
|
-
/dashboard/data?range=
|
|
18
|
+
/dashboard/data?range=Rd — band numbers, snapshot series, digest
|
|
19
19
|
/dashboard/field — the whole field (memories + real links)
|
|
20
|
-
/dashboard/events?days=
|
|
20
|
+
/dashboard/events?days=R — the replay ledger (real nights)
|
|
21
21
|
/health + /health/detail — live dot (30 s poll) + model label
|
|
22
22
|
/account + /account/token — the account menu (harvested from 0.20.4)
|
|
23
23
|
/search /memory /update /delete /identity — the browser + agent rail
|
|
24
|
+
(R = the range selector's value: 30 by default, 7/90/180 opt-in — #452)
|
|
24
25
|
|
|
25
26
|
PHASE 2 (#422 — operator surfaces) LANDED:
|
|
26
27
|
- Capture health: real /distill accounting rows (posts · sessions · MB ·
|
|
27
28
|
held per machine × harness) via /dashboard/data capture_health; the
|
|
28
29
|
phase-1 counts-only rows remain as the degrade.
|
|
30
|
+
Fix round 7 (#409, owner ruling 2026-09-14 — "the normal 30 day as the
|
|
31
|
+
other cards"): the card's face is the ROLLING window (window_rows,
|
|
32
|
+
window_days), the newest night rides as a per-row secondary suffix;
|
|
33
|
+
retention == window (CAPTURE_HEALTH_WINDOW_DAYS).
|
|
29
34
|
- Activity bars: five segments (added + merged + the three graduation
|
|
30
35
|
deltas from snapshot stage_counts; two-segment rendering when buckets
|
|
31
36
|
are absent).
|
|
@@ -132,6 +137,30 @@
|
|
|
132
137
|
stays marked; the visible word failed to communicate). Vocabulary
|
|
133
138
|
amendment: Low / HEALTHY / Overfetching. Marker + trend stay.
|
|
134
139
|
|
|
140
|
+
REPLAY + RANGE (#452, owner directives 2026-09-16):
|
|
141
|
+
- 30-DAY DEFAULT FOR ALL VIEWS: the replay ledger, the band and the
|
|
142
|
+
snapshot series all default to 30 days (server EVENTS_DEFAULT_DAYS
|
|
143
|
+
90 → 30; the client's every fetch is selector-driven). Longer
|
|
144
|
+
ranges are OPT-IN via a compact 7 / 30 / 90 / 180 segmented control
|
|
145
|
+
in the replay foot — explicit click only, never auto-loaded
|
|
146
|
+
(SUPERSEDES the 2026-09-12 no-dropdowns ruling). No persistence:
|
|
147
|
+
every load starts at 30 (loads fast).
|
|
148
|
+
- PLAY ONCE, THEN REST: the replay auto-starts as before (~3.6 s after
|
|
149
|
+
boot), walks the ledger ONCE to today, and STOPS there — no loop
|
|
150
|
+
(SUPERSEDES the 2026-09-13 auto-loop/no-buttons ruling). Controls in
|
|
151
|
+
the replay foot, styled like "return to now": pause/resume (holds
|
|
152
|
+
position mid-cruise; hides once the playback completed — nothing is
|
|
153
|
+
playing) and "replay again" (appears once completed; restarts from
|
|
154
|
+
the range start). Drag/scrub behavior is UNCHANGED: a manual take
|
|
155
|
+
pauses the cruise and re-engages ~12 s after idle, continuing
|
|
156
|
+
toward today — where it now stops instead of looping.
|
|
157
|
+
- DATE LABEL ON THE SCRUBBER: the cursor dot carries a small mono
|
|
158
|
+
label above it (the night being viewed, "now" at the end).
|
|
159
|
+
- TRUTH-MANAGEMENT CAPTIONS: Superseded / Retracted / Rewritten ride
|
|
160
|
+
the events ledger alongside learned/enriched/merged (server-side
|
|
161
|
+
sources in dashboard.ts; superseded/retracted bloom in the fading
|
|
162
|
+
color — demoted — rewritten in the truth color).
|
|
163
|
+
|
|
135
164
|
REMAINING DEGRADATIONS (planned, per the #409 phase table — not gaps):
|
|
136
165
|
- Recall card: no raw shown/used counts (the per-memory adoption sums
|
|
137
166
|
are not in any payload yet).
|
|
@@ -561,11 +590,25 @@
|
|
|
561
590
|
pointer-events:none; transition:opacity 1.4s; z-index:5;
|
|
562
591
|
}
|
|
563
592
|
.hint em{ font-style:normal; color:var(--dim); }
|
|
564
|
-
/* replay — inside the brain panel; drag
|
|
593
|
+
/* replay — inside the brain panel; drag to scrub, or let the cruise walk
|
|
594
|
+
the ledger ONCE to today (#452: range pills + pause / replay again) */
|
|
565
595
|
.replay-foot{ border-top:1px solid var(--line); padding:7px 12px 8px; flex:none; background:color-mix(in srgb, var(--panel) 88%, transparent); }
|
|
566
596
|
#replay{ display:flex; align-items:center; gap:12px; }
|
|
567
597
|
#replay.off{ opacity:.45; pointer-events:none; }
|
|
598
|
+
/* the range selector stays LIVE even when the instrument is off — a wider
|
|
599
|
+
window can reveal ≥2 nights and revive the replay (#452) */
|
|
600
|
+
#replay.off #rp-range{ opacity:1; pointer-events:auto; }
|
|
568
601
|
#rp-label{ font-size:10px; color:var(--faint); text-transform:uppercase; letter-spacing:.14em; white-space:nowrap; flex:none; }
|
|
602
|
+
/* #452 range selector — the .idpill.seg idiom (mono pills, active = green) */
|
|
603
|
+
#rp-range{ display:inline-flex; gap:3px; flex:none; }
|
|
604
|
+
.rp-seg{
|
|
605
|
+
font-size:9px; font-family:var(--mono); letter-spacing:.05em; color:var(--faint);
|
|
606
|
+
border:1px solid var(--line2); border-radius:999px; padding:3px 8px; white-space:nowrap;
|
|
607
|
+
transition:color .15s, border-color .15s;
|
|
608
|
+
}
|
|
609
|
+
.rp-seg:hover{ color:var(--dim); }
|
|
610
|
+
.rp-seg[aria-pressed="true"]{ color:var(--green-mid); border-color:color-mix(in srgb, var(--green) 40%, transparent); }
|
|
611
|
+
html:not([data-theme="light"]) .rp-seg[aria-pressed="true"]{ box-shadow:0 0 10px rgba(61,255,122,.12); }
|
|
569
612
|
#rp-track{ flex:1; position:relative; height:26px; cursor:pointer; touch-action:none; min-width:60px; }
|
|
570
613
|
#rp-rail{ position:absolute; left:0; right:0; top:11px; height:3px; border-radius:2px; background:var(--tbar-bg); }
|
|
571
614
|
#rp-fill{ position:absolute; left:0; top:11px; height:3px; border-radius:2px; width:100%;
|
|
@@ -577,14 +620,23 @@
|
|
|
577
620
|
}
|
|
578
621
|
html:not([data-theme="light"]) #rp-cursor{ background:#0d1f14; box-shadow:0 0 12px rgba(61,255,122,.7); }
|
|
579
622
|
html[data-theme="light"] #rp-cursor{ box-shadow:0 1px 4px rgba(56,54,42,.3); }
|
|
623
|
+
/* #452: the night being viewed, floating above the scrub dot (the
|
|
624
|
+
#rp-dates span styling; overflow is visible — it may overlap the canvas
|
|
625
|
+
bottom edge like .hint does) */
|
|
626
|
+
#rp-date{
|
|
627
|
+
position:absolute; top:-13px; transform:translateX(-50%); white-space:nowrap;
|
|
628
|
+
font-size:9px; color:var(--faint); font-family:var(--mono); pointer-events:none; z-index:3;
|
|
629
|
+
display:none;
|
|
630
|
+
}
|
|
631
|
+
#rp-date.show{ display:block; }
|
|
580
632
|
.rp-dot{ position:absolute; top:9px; width:5px; height:5px; border-radius:50%; margin-left:-2.5px; background:var(--line2); }
|
|
581
633
|
.rp-dot.g{ background:var(--green-mid); }
|
|
582
634
|
.rp-dot.w{ background:var(--dim); }
|
|
583
635
|
#rp-dates{ position:absolute; left:0; right:0; top:21px; height:12px; }
|
|
584
636
|
#rp-dates span{ position:absolute; transform:translateX(-50%); font-size:9px; color:var(--faint); font-family:var(--mono); }
|
|
585
|
-
#rp-now{ display:none; font-size:10.5px; color:var(--green-mid); border:1px solid color-mix(in srgb, var(--green) 40%, transparent); border-radius:999px; padding:5px 11px; white-space:nowrap; }
|
|
586
|
-
#rp-now.show{ display:inline-block; }
|
|
587
|
-
html:not([data-theme="light"]) #rp-now{ box-shadow:0 0 12px rgba(61,255,122,.15); }
|
|
637
|
+
#rp-now,#rp-pause,#rp-again{ display:none; font-size:10.5px; color:var(--green-mid); border:1px solid color-mix(in srgb, var(--green) 40%, transparent); border-radius:999px; padding:5px 11px; white-space:nowrap; }
|
|
638
|
+
#rp-now.show,#rp-pause.show,#rp-again.show{ display:inline-block; }
|
|
639
|
+
html:not([data-theme="light"]) #rp-now,html:not([data-theme="light"]) #rp-pause,html:not([data-theme="light"]) #rp-again{ box-shadow:0 0 12px rgba(61,255,122,.15); }
|
|
588
640
|
|
|
589
641
|
/* ---- agents rail — the ONLY thing beside the brain, rightmost ---- */
|
|
590
642
|
#agentscol{
|
|
@@ -768,7 +820,7 @@
|
|
|
768
820
|
/* capture health — phase 2: per machine × harness rows from /distill
|
|
769
821
|
accounting (posts/sessions/MB/held); the phase-1 counts-only rows remain
|
|
770
822
|
as the degrade for old servers / nothing recorded yet */
|
|
771
|
-
.cap-row2{ display:flex; align-items:center; gap:9px; padding:2.5px 0; border-bottom:1px solid color-mix(in srgb, var(--line) 60%, transparent); }
|
|
823
|
+
.cap-row2{ display:flex; flex-wrap:wrap; align-items:center; gap:1px 9px; padding:2.5px 0; border-bottom:1px solid color-mix(in srgb, var(--line) 60%, transparent); }
|
|
772
824
|
.cap-row2:last-of-type{ border-bottom:none; }
|
|
773
825
|
.cap-dot{ width:6px; height:6px; border-radius:50%; flex:none; background:var(--green-mid); }
|
|
774
826
|
.cap-row2 .hn{ font:11px var(--mono); color:var(--text); flex:0 0 148px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
@@ -779,6 +831,12 @@
|
|
|
779
831
|
.cap-bar .fill{ height:100%; border-radius:2.5px; background:var(--green-mid); }
|
|
780
832
|
.cap-row2 .st{ font-size:10px; color:var(--faint); font-family:var(--mono); white-space:nowrap; }
|
|
781
833
|
.cap-row2 .st b{ color:var(--dim); font-weight:500; }
|
|
834
|
+
/* fix round 7: the newest-night sub-line on the rolling-window rows — a
|
|
835
|
+
full-basis child wraps under the row's primary line (the suffix does NOT
|
|
836
|
+
fit beside "N sess · X MB" without pushing past the card edge); same
|
|
837
|
+
faint mono tone, theme token only, both themes free. Rows without a
|
|
838
|
+
newest night stay single-line. */
|
|
839
|
+
.cap-row2 .cap-ton{ flex-basis:100%; text-align:right; font:10px var(--mono); color:var(--faint); line-height:1.1; }
|
|
782
840
|
.cap-held{ color:var(--amber); margin-left:5px; }
|
|
783
841
|
/* #423 phase 3: the capture-pause badge — the same amber family as the
|
|
784
842
|
held badge (an operator state, not an error) */
|
|
@@ -963,6 +1021,7 @@
|
|
|
963
1021
|
.brow{ grid-template-columns:1fr; }
|
|
964
1022
|
.mrow .sn{ font-size:10px; }
|
|
965
1023
|
#rp-label{ display:none; }
|
|
1024
|
+
#replay{ flex-wrap:wrap; } /* #452: range pills + controls wrap rather than crush the track */
|
|
966
1025
|
#rp-dates span:nth-child(n+3){ display:none; }
|
|
967
1026
|
.acct-trigger .who{ display:none; }
|
|
968
1027
|
.field-head{ padding:8px 12px 7px; }
|
|
@@ -1180,13 +1239,17 @@
|
|
|
1180
1239
|
</div>
|
|
1181
1240
|
<div class="replay-foot">
|
|
1182
1241
|
<div id="replay">
|
|
1242
|
+
<div id="rp-range" role="group" aria-label="range"></div>
|
|
1183
1243
|
<span id="rp-label">nights</span>
|
|
1184
1244
|
<div id="rp-track">
|
|
1185
1245
|
<div id="rp-rail"></div>
|
|
1186
1246
|
<div id="rp-fill"></div>
|
|
1187
1247
|
<div id="rp-dates"></div>
|
|
1188
1248
|
<div id="rp-cursor"></div>
|
|
1249
|
+
<span id="rp-date"></span>
|
|
1189
1250
|
</div>
|
|
1251
|
+
<button id="rp-pause">pause</button>
|
|
1252
|
+
<button id="rp-again">replay again</button>
|
|
1190
1253
|
<button id="rp-now">return to now</button>
|
|
1191
1254
|
</div>
|
|
1192
1255
|
</div>
|
|
@@ -1307,7 +1370,7 @@ if(STILL){ document.body.classList.remove('booting'); document.body.classList.ad
|
|
|
1307
1370
|
/* =====================================================================
|
|
1308
1371
|
The approved #409 r6-flight engine, ported to live data. One canvas,
|
|
1309
1372
|
three verbs (look / travel / time) plus the quiet fourth: WATCH — ambient
|
|
1310
|
-
signals loop slowly along REAL stored links,
|
|
1373
|
+
signals loop slowly along REAL stored links, always on.
|
|
1311
1374
|
Stages are DERIVED presentation (server-side via /dashboard/field) over
|
|
1312
1375
|
the one numeric strength signal — never stored state.
|
|
1313
1376
|
===================================================================== */
|
|
@@ -1328,9 +1391,15 @@ function fmtTokens(n){
|
|
|
1328
1391
|
return String(n);
|
|
1329
1392
|
}
|
|
1330
1393
|
/* deterministic per-id randomness — the same memory always lands in the
|
|
1331
|
-
same place across reloads (positions are content of the field, not noise)
|
|
1394
|
+
same place across reloads (positions are content of the field, not noise).
|
|
1395
|
+
#465: the salt hashes at the FRONT (hash32(salt+':'+s)) — FNV-1a does not
|
|
1396
|
+
diffuse a trailing-byte difference, and salts 1/2/3 differ only in their
|
|
1397
|
+
last character, so the old trailing form coupled angle to radius at
|
|
1398
|
+
Pearson r=0.953 (four spiral arms per domain). Salt-first measures
|
|
1399
|
+
r=0.019 (noise). Positions shift once (hash-derived content), then
|
|
1400
|
+
re-stabilize. */
|
|
1332
1401
|
function hash32(s){ let h=2166136261; for(let i=0;i<s.length;i++){ h^=s.charCodeAt(i); h=Math.imul(h,16777619);} return h>>>0; }
|
|
1333
|
-
function rand01(s,salt){ return hash32(
|
|
1402
|
+
function rand01(s,salt){ return hash32(salt+':'+s)/4294967296; }
|
|
1334
1403
|
|
|
1335
1404
|
/* ---------- token handling (harvested verbatim from the 0.20.4 console:
|
|
1336
1405
|
?token= handoff stripped from the URL, localStorage persistence, 401 →
|
|
@@ -1424,9 +1493,12 @@ function bubbleRadius(s){ return 6 + Math.sqrt(clamp(s??0,0,1))*3; }
|
|
|
1424
1493
|
function depthSizeDivisor(dz){ return Math.pow(Math.max(.14,dz),0.35); }
|
|
1425
1494
|
|
|
1426
1495
|
/* ---------- live-data state ---------- */
|
|
1427
|
-
let
|
|
1496
|
+
let RANGE=30; // days — the range selector's value (#452; 30-day default for
|
|
1497
|
+
// ALL views, owner directive 2026-09-16). NO persistence: every
|
|
1498
|
+
// load starts at 30 — it loads fast; longer is opt-in per click.
|
|
1499
|
+
let D=null; // /dashboard/data?range=RANGE d — band, series, digest
|
|
1428
1500
|
let F=null; // /dashboard/field
|
|
1429
|
-
let EV=null; // /dashboard/events?days=
|
|
1501
|
+
let EV=null; // /dashboard/events?days=RANGE — the replay ledger
|
|
1430
1502
|
let M=null; // /dashboard/model (#422) — null on failure: label-only degrade, no configure pill
|
|
1431
1503
|
let TOTAL=0, removedNow=0;
|
|
1432
1504
|
/* the store's static LIVE count (headline.live_memories ?? total): the
|
|
@@ -1637,13 +1709,19 @@ function buildWorld(){
|
|
|
1637
1709
|
|
|
1638
1710
|
// the flattened replay ledger (events within a night fire in cruise order);
|
|
1639
1711
|
// added/merged reuse the staggered born/merge times so blooms and births
|
|
1640
|
-
// coincide; enriched/linked carry their own salted staggers.
|
|
1712
|
+
// coincide; enriched/linked carry their own salted staggers. #452 adds the
|
|
1713
|
+
// truth-management captions — stagger order enriched .55 < retracted .65 <
|
|
1714
|
+
// superseded .70 < rewritten .75 < learned .80 keeps the cruise sane (each
|
|
1715
|
+
// new type gets its own salt, so per-id positions never collide).
|
|
1641
1716
|
EVENTS = [];
|
|
1642
1717
|
NIGHTS.forEach((n,i)=>{
|
|
1643
1718
|
for(const id of n.added) EVENTS.push({t:bornOf.get(id), type:'learned', id, cap:'Learned — “'+trunc(titleOf(id),52)+'”'});
|
|
1644
1719
|
for(const id of n.enriched) EVENTS.push({t:i+.55+stag(id,19)*.08, type:'enriched', id, cap:'Enriched — “'+trunc(titleOf(id),52)+'”'});
|
|
1645
1720
|
for(const mg of n.merged) EVENTS.push({t:mergeOf.get(mg.loser).t, type:'merged', loser:mg.loser, id:mg.loser, cap:'Merged — “'+trunc(titleOf(mg.loser),40)+'”'});
|
|
1646
1721
|
for(const lk of n.linked) EVENTS.push({t:i+.90+stag(lk.a+lk.b,23)*.08, type:'linked', a:lk.a, b:lk.b, cap:null});
|
|
1722
|
+
for(const sp of n.superseded) EVENTS.push({t:i+.70+stag(sp.old,29)*.08, type:'superseded', id:sp.old, cap:'Superseded — “'+trunc(titleOf(sp.old),52)+'”'});
|
|
1723
|
+
for(const rt of n.retracted) EVENTS.push({t:i+.65+stag(rt.old,31)*.08, type:'retracted', id:rt.old, cap:'Retracted — “'+trunc(titleOf(rt.old),52)+'”'});
|
|
1724
|
+
for(const id of n.rewritten) EVENTS.push({t:i+.75+stag(id,37)*.08, type:'rewritten', id, cap:'Rewritten — “'+trunc(titleOf(id),52)+'”'});
|
|
1647
1725
|
});
|
|
1648
1726
|
EVENTS.sort((x,y)=>x.t-y.t);
|
|
1649
1727
|
|
|
@@ -2487,14 +2565,21 @@ function toast(msg, ms){
|
|
|
2487
2565
|
function wake(){ lastActivity=performance.now(); }
|
|
2488
2566
|
|
|
2489
2567
|
/* ---------- replay — time travel: drag to take the helm, else the G1
|
|
2490
|
-
auto-cruise walks the REAL event ledger (one memory action at a time
|
|
2491
|
-
|
|
2568
|
+
auto-cruise walks the REAL event ledger (one memory action at a time)
|
|
2569
|
+
ONCE to today, then rests there (#452, owner ruling 2026-09-16 —
|
|
2570
|
+
supersedes the 2026-09-13 auto-loop). Pause/resume + "replay again" sit
|
|
2571
|
+
in the foot beside "return to now"; a manual take still pauses the
|
|
2572
|
+
cruise and re-engages ~12 s after idle, continuing toward today — where
|
|
2573
|
+
it now stops instead of looping.
|
|
2492
2574
|
With fewer than 2 nights the instrument renders its "now" state and
|
|
2493
2575
|
disables itself (degrade quietly). ---------- */
|
|
2494
2576
|
const track=el('rp-track');
|
|
2495
2577
|
function trackFrac(e){ const r=track.getBoundingClientRect(); return clamp((e.clientX-r.left)/r.width,0,1); }
|
|
2496
2578
|
let autoPlay=false, autoEvIdx=0, autoNextAt=0, manualUntil=3600, pendingApply=null;
|
|
2579
|
+
let paused=false; /* #452: pause holds the cruise position; resume continues */
|
|
2580
|
+
let played=false; /* #452: a full playback completed → rest at today, never loop */
|
|
2497
2581
|
function manualTake(){ autoPlay=false; pendingApply=null; manualUntil=performance.now()+12000; wake(); }
|
|
2582
|
+
function setPaused(v){ paused=v; el('rp-pause').textContent=paused?'resume':'pause'; }
|
|
2498
2583
|
/* HERALD — before each cruise step's change, a bright pulse flies in along
|
|
2499
2584
|
a REAL link touching the target (else from its nearest same-domain
|
|
2500
2585
|
neighbor) and LANDS: the update we see is the arrival */
|
|
@@ -2518,18 +2603,25 @@ function heraldTo(id){
|
|
|
2518
2603
|
signals.push({ path:p, i:0, s:0, dur:.45, tail:[], showcase:true, herald:true }); /* dur is seconds */
|
|
2519
2604
|
}
|
|
2520
2605
|
function autoTick(now){
|
|
2521
|
-
if(REDUCED||booting||!REPLAY_OK) return;
|
|
2606
|
+
if(REDUCED||booting||!REPLAY_OK||paused) return;
|
|
2522
2607
|
if(!autoPlay){ if(now>manualUntil && !scrubbing) autoPlay=true; else return; }
|
|
2523
2608
|
if(pendingApply){
|
|
2524
2609
|
if(now>=pendingApply.at){ seek(pendingApply.t); pendingApply=null; autoNextAt=now+820; }
|
|
2525
2610
|
return; /* the herald is in flight */
|
|
2526
2611
|
}
|
|
2527
2612
|
if(now<autoNextAt) return;
|
|
2528
|
-
if(T>=NOW_T-.001){
|
|
2613
|
+
if(T>=NOW_T-.001){
|
|
2614
|
+
/* a beat at "now": re-kick to the range start ONLY before the first
|
|
2615
|
+
full playback; after it, REST here — plays once, never loops (#452) */
|
|
2616
|
+
if(played){ autoPlay=false; autoNextAt=now+3200; return; }
|
|
2529
2617
|
autoEvIdx=0; autoNextAt=now+3200; seek(.001); return;
|
|
2530
2618
|
}
|
|
2531
2619
|
while(autoEvIdx<EVENTS.length && EVENTS[autoEvIdx].t<=T+.0001) autoEvIdx++;
|
|
2532
|
-
if(autoEvIdx>=EVENTS.length){
|
|
2620
|
+
if(autoEvIdx>=EVENTS.length){
|
|
2621
|
+
/* ledger exhausted: arrive at today and complete the playback (the
|
|
2622
|
+
flags go BEFORE the seek so onTChanged's control swap sees them) */
|
|
2623
|
+
autoNextAt=now+3200; played=true; autoPlay=false; seek(WINDOW); return;
|
|
2624
|
+
}
|
|
2533
2625
|
const ev=EVENTS[autoEvIdx++];
|
|
2534
2626
|
if(ev.id && signals.length<8){ heraldTo(ev.id); pendingApply={t:ev.t+.001, at:now+430}; }
|
|
2535
2627
|
else { seek(ev.t+.001); autoNextAt=now+820; }
|
|
@@ -2550,7 +2642,10 @@ function onTChanged(fromSeek){
|
|
|
2550
2642
|
const b=mems.get(ev.id);
|
|
2551
2643
|
if(!b||!aliveAt(b,ev.t)||b._sx===undefined) continue;
|
|
2552
2644
|
if(blooms.length>=6) break;
|
|
2553
|
-
|
|
2645
|
+
/* #452: superseded/retracted bloom in the FADING color (demoted
|
|
2646
|
+
claims); rewritten falls to the default truth color (a rewrite is
|
|
2647
|
+
the claim surviving, corrected) */
|
|
2648
|
+
const rgb= ev.type==='merged'||ev.type==='superseded'||ev.type==='retracted'? PAL.stages.fading.rgb : (ev.type==='learned'? PAL.stages.forming.rgb : PAL.stages.truth.rgb);
|
|
2554
2649
|
blooms.push({x:b._sx,y:b._sy,r0:2,r1:24+b._sr,t0:performance.now(),dur:1400,cap:ev.cap,rgb});
|
|
2555
2650
|
}
|
|
2556
2651
|
}
|
|
@@ -2559,6 +2654,18 @@ function onTChanged(fromSeek){
|
|
|
2559
2654
|
el('rp-cursor').style.left=(T/WINDOW*100)+'%';
|
|
2560
2655
|
const isNow=T>=NOW_T-.001;
|
|
2561
2656
|
el('rp-now').classList.toggle('show',!isNow&&REPLAY_OK);
|
|
2657
|
+
/* #452 date label above the scrub dot — same left % as the cursor, the
|
|
2658
|
+
night being viewed ("now" at the end); hidden when there is no ledger */
|
|
2659
|
+
const dlab=el('rp-date');
|
|
2660
|
+
if(REPLAY_OK&&NIGHTS.length){
|
|
2661
|
+
dlab.style.left=(T/WINDOW*100)+'%';
|
|
2662
|
+
dlab.textContent=isNow?'now':fmtDay(NIGHTS[Math.min(Math.floor(T),NIGHTS.length-1)].date);
|
|
2663
|
+
dlab.classList.add('show');
|
|
2664
|
+
}else dlab.classList.remove('show');
|
|
2665
|
+
/* #452 lifecycle pills: pause while a playback is live, "replay again"
|
|
2666
|
+
once it completed — at most ONE of them beside "return to now" */
|
|
2667
|
+
el('rp-pause').classList.toggle('show',REPLAY_OK&&!played);
|
|
2668
|
+
el('rp-again').classList.toggle('show',REPLAY_OK&&played);
|
|
2562
2669
|
updateMeasures();
|
|
2563
2670
|
/* #422 owner ruling: the overview counter is STATIC (the store's live
|
|
2564
2671
|
count, set once at boot + after writes) — scrubbing the replay no longer
|
|
@@ -2566,10 +2673,13 @@ function onTChanged(fromSeek){
|
|
|
2566
2673
|
dirty=true;
|
|
2567
2674
|
}
|
|
2568
2675
|
/* warp-to-now — accelerates through every remaining event, blooms firing,
|
|
2569
|
-
and arrives ~2.4 s later (ease in-out)
|
|
2676
|
+
and arrives ~2.4 s later (ease in-out). #452: arriving at now by hand
|
|
2677
|
+
completes the playback too — the cruise rests instead of looping after */
|
|
2570
2678
|
let warpUntil=0, warpFrom=0;
|
|
2571
2679
|
function warpToNow(){
|
|
2572
2680
|
manualTake();
|
|
2681
|
+
setPaused(false);
|
|
2682
|
+
played=true;
|
|
2573
2683
|
if(REDUCED||T>=NOW_T-.001||!REPLAY_OK){ seek(WINDOW); return; }
|
|
2574
2684
|
warpFrom=T; warpUntil=performance.now()+2400;
|
|
2575
2685
|
}
|
|
@@ -2582,6 +2692,72 @@ function warpTick(now){
|
|
|
2582
2692
|
}
|
|
2583
2693
|
el('rp-now').onclick=warpToNow;
|
|
2584
2694
|
|
|
2695
|
+
/* #452 — pause/resume + replay again. Pause holds the cruise position
|
|
2696
|
+
(autoTick returns early, pending heralds included); resume continues
|
|
2697
|
+
toward today. Replay again restarts from the range start and plays once.
|
|
2698
|
+
Visibility is onTChanged's job: pause renders only while a playback is
|
|
2699
|
+
live, "replay again" only once one completed — least clutter. */
|
|
2700
|
+
el('rp-pause').onclick=()=>{
|
|
2701
|
+
setPaused(!paused);
|
|
2702
|
+
if(!paused) autoPlay=true; /* resume continues the cruise immediately */
|
|
2703
|
+
wake();
|
|
2704
|
+
};
|
|
2705
|
+
el('rp-again').onclick=()=>{
|
|
2706
|
+
manualTake(); setPaused(false);
|
|
2707
|
+
played=false; autoEvIdx=0; pendingApply=null; autoPlay=true;
|
|
2708
|
+
seek(.001); wake();
|
|
2709
|
+
};
|
|
2710
|
+
|
|
2711
|
+
/* #452 — the range selector: 7/30/90/180 days (the owner-fixed option
|
|
2712
|
+
set), default 30, NO persistence — every load starts at 30 (loads fast).
|
|
2713
|
+
A click refetches BOTH range-scoped sources and rebuilds the world
|
|
2714
|
+
exactly like boot — the agents rail included (its "last night" line
|
|
2715
|
+
reads NIGHTS, so it must follow the new window); the replay then
|
|
2716
|
+
auto-starts on the new range via the idle re-engage (mirrors boot).
|
|
2717
|
+
Fail-soft: a failed fetch toasts, keeps the OLD data and the OLD pills
|
|
2718
|
+
— never a half-switched view. One switch at a time (refreshAfterWrite's
|
|
2719
|
+
refreshInFlight pattern): a click while a switch is in flight is
|
|
2720
|
+
IGNORED — a racing second fetch could resolve out of order and leave
|
|
2721
|
+
pills and data disagreeing. */
|
|
2722
|
+
const RANGE_OPTIONS=[7,30,90,180];
|
|
2723
|
+
function renderRangePills(){
|
|
2724
|
+
const host=el('rp-range'); host.innerHTML='';
|
|
2725
|
+
for(const n of RANGE_OPTIONS){
|
|
2726
|
+
const b=document.createElement('button');
|
|
2727
|
+
b.type='button'; b.className='rp-seg'; b.textContent=n+'d';
|
|
2728
|
+
b.title='last '+n+' days';
|
|
2729
|
+
b.setAttribute('aria-pressed', String(n===RANGE));
|
|
2730
|
+
b.onclick=()=>{ if(n!==RANGE) switchRange(n); };
|
|
2731
|
+
host.appendChild(b);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
let rangeSwitchInFlight=false;
|
|
2735
|
+
async function switchRange(n){
|
|
2736
|
+
if(rangeSwitchInFlight) return; rangeSwitchInFlight=true;
|
|
2737
|
+
const prev=RANGE; RANGE=n; renderRangePills();
|
|
2738
|
+
try{
|
|
2739
|
+
const [dd,ee]=await Promise.all([
|
|
2740
|
+
api('/dashboard/data?range='+RANGE+'d'),
|
|
2741
|
+
api('/dashboard/events?days='+RANGE),
|
|
2742
|
+
]);
|
|
2743
|
+
D=dd;
|
|
2744
|
+
if(ee&&Array.isArray(ee.nights)){ EV=ee; NIGHTS=EV.nights; } else { NIGHTS=[]; }
|
|
2745
|
+
}catch(e){
|
|
2746
|
+
RANGE=prev; renderRangePills();
|
|
2747
|
+
toast('Range switch failed: '+e.message);
|
|
2748
|
+
return;
|
|
2749
|
+
}finally{
|
|
2750
|
+
rangeSwitchInFlight=false;
|
|
2751
|
+
}
|
|
2752
|
+
buildWorld(); buildPaths(); buildTimeline(); renderBand(); buildRoll(); buildCapture(); buildAgents();
|
|
2753
|
+
/* reset the replay over the new window (boot's pattern): rest at "now"
|
|
2754
|
+
first, the idle re-engage then walks the new range once from the start */
|
|
2755
|
+
T=WINDOW; lastT=WINDOW; played=false; autoEvIdx=0; pendingApply=null; autoPlay=false;
|
|
2756
|
+
setPaused(false);
|
|
2757
|
+
onTChanged(true);
|
|
2758
|
+
}
|
|
2759
|
+
renderRangePills();
|
|
2760
|
+
|
|
2585
2761
|
/* the timeline: dots per night (green when busy adding, grey when the
|
|
2586
2762
|
night merged ≥3 — the prototype's thresholds), date labels at the ends */
|
|
2587
2763
|
function buildTimeline(){
|
|
@@ -2594,6 +2770,7 @@ function buildTimeline(){
|
|
|
2594
2770
|
dots.style.cssText='position:absolute;inset:0;pointer-events:none;';
|
|
2595
2771
|
if(REPLAY_OK){
|
|
2596
2772
|
label.textContent=NIGHTS.length+' nights';
|
|
2773
|
+
el('replay').classList.remove('off'); /* a range switch can revive the instrument (#452) */
|
|
2597
2774
|
const s1=document.createElement('span'); s1.style.left='0%'; s1.style.transform='none';
|
|
2598
2775
|
s1.textContent=fmtDay(NIGHTS[0].date); dates.appendChild(s1);
|
|
2599
2776
|
const s2=document.createElement('span'); s2.style.left='100%'; s2.style.transform='translateX(-100%)';
|
|
@@ -2603,7 +2780,10 @@ function buildTimeline(){
|
|
|
2603
2780
|
d.style.left=((i+.5)/WINDOW*100)+'%';
|
|
2604
2781
|
if(mergedL[i]>=3) d.classList.add('w');
|
|
2605
2782
|
else if(learnedL[i]>=8) d.classList.add('g');
|
|
2606
|
-
d.title=fmtDay(n.date)+' — learned '+(learnedL[i]||0)+(enrichedL[i]?' · enriched '+enrichedL[i]:'')+(mergedL[i]?' · merged '+mergedL[i]:'')
|
|
2783
|
+
d.title=fmtDay(n.date)+' — learned '+(learnedL[i]||0)+(enrichedL[i]?' · enriched '+enrichedL[i]:'')+(mergedL[i]?' · merged '+mergedL[i]:'')
|
|
2784
|
+
+(n.superseded.length?' · superseded '+n.superseded.length:'')
|
|
2785
|
+
+(n.retracted.length?' · retracted '+n.retracted.length:'')
|
|
2786
|
+
+(n.rewritten.length?' · rewritten '+n.rewritten.length:'');
|
|
2607
2787
|
frag.appendChild(d);
|
|
2608
2788
|
});
|
|
2609
2789
|
}else{
|
|
@@ -3231,7 +3411,14 @@ function buildRoll(){
|
|
|
3231
3411
|
|
|
3232
3412
|
/* ---------- capture health — phase 2: per machine × harness rows from the
|
|
3233
3413
|
/dashboard/data capture_health accounting; the counts-only rows remain
|
|
3234
|
-
the degrade for old servers / nothing recorded yet
|
|
3414
|
+
the degrade for old servers / nothing recorded yet.
|
|
3415
|
+
Fix round 7 (owner ruling 2026-09-14 — "the normal 30 day as the other
|
|
3416
|
+
cards"): the card's face is the ROLLING window (capture_health
|
|
3417
|
+
window_rows, summed over the last window_days calendar days); the newest
|
|
3418
|
+
night rides as a per-row secondary suffix ("tonight: N sess" / the
|
|
3419
|
+
night's date when it is not today). Degradation ladder: pre-round-7
|
|
3420
|
+
server (no window_rows) → the newest-night view below; no activity rows
|
|
3421
|
+
at all → the counts-only rows. ---------- */
|
|
3235
3422
|
/* ---------- machine × harness grouping (#424 review fixes 4+5 + the owner's
|
|
3236
3423
|
model-B direction): source_agent is "harness/profile" and each context may
|
|
3237
3424
|
carry a source_machine stamp (migration v15+). The rows a stats card and
|
|
@@ -3343,6 +3530,72 @@ function buildCapture(){
|
|
|
3343
3530
|
host.innerHTML='';
|
|
3344
3531
|
const sub=el('cap-sub');
|
|
3345
3532
|
const ch=D&&D.capture_health?D.capture_health:null;
|
|
3533
|
+
/* fix round 7: the rolling-window view — the card shows the normal 30 day
|
|
3534
|
+
like the band's other cards. window_rows is ABSENT on a pre-round-7
|
|
3535
|
+
server (and empty when nothing is recorded) → fall through to the
|
|
3536
|
+
newest-night view / counts degrade below. */
|
|
3537
|
+
if(ch&&Array.isArray(ch.window_rows)&&ch.window_rows.length){
|
|
3538
|
+
const wd=ch.window_days||30;
|
|
3539
|
+
if(sub) sub.textContent='last '+wd+' days';
|
|
3540
|
+
/* group the window's machine × agent rows into machine × harness
|
|
3541
|
+
bundles — same grouping as every other card surface */
|
|
3542
|
+
const groups=new Map();
|
|
3543
|
+
for(const r of ch.window_rows){
|
|
3544
|
+
const agent=String(r.agent||'unknown');
|
|
3545
|
+
const harness=agent.split('/')[0]||agent;
|
|
3546
|
+
const key=(r.machine||'')+'|'+harness;
|
|
3547
|
+
let g=groups.get(key);
|
|
3548
|
+
if(!g){ g={machine:r.machine||null, harness, posts:0, sessions:0, bytes:0, held:0, retried:0, agents:[]}; groups.set(key,g); }
|
|
3549
|
+
g.posts+=r.posts||0; g.sessions+=r.sessions||0; g.bytes+=r.bytes||0;
|
|
3550
|
+
g.held+=r.held||0; g.retried+=r.retried||0;
|
|
3551
|
+
g.agents.push(agent+' — '+r.posts+' post'+(r.posts===1?'':'s')+' · '+r.sessions+' sess · '+mbFmt(r.bytes||0)
|
|
3552
|
+
+(r.held?' · '+r.held+' held':'')+(r.retried?' · '+r.retried+' retried':''));
|
|
3553
|
+
}
|
|
3554
|
+
/* newest-night secondary: per-bundle sessions + held off capture_health
|
|
3555
|
+
rows (the newest day with rows). held keys the badge — a held post is
|
|
3556
|
+
actionable NOW (the client retries it next run); the window's own
|
|
3557
|
+
held/retried live in the row tooltip. */
|
|
3558
|
+
const night=new Map();
|
|
3559
|
+
if(ch.day&&Array.isArray(ch.rows)) for(const r of ch.rows){
|
|
3560
|
+
const agent=String(r.agent||'unknown');
|
|
3561
|
+
const harness=agent.split('/')[0]||agent;
|
|
3562
|
+
const key=(r.machine||'')+'|'+harness;
|
|
3563
|
+
const prev=night.get(key)||{sessions:0,held:0};
|
|
3564
|
+
prev.sessions+=r.sessions||0; prev.held+=r.held||0;
|
|
3565
|
+
night.set(key,prev);
|
|
3566
|
+
}
|
|
3567
|
+
const today=new Date().toISOString().slice(0,10);
|
|
3568
|
+
const nightLabel=ch.day===today?'tonight':fmtDay(ch.day);
|
|
3569
|
+
const gs2=[...groups.values()].sort((a,b)=>b.bytes-a.bytes);
|
|
3570
|
+
const maxB=gs2[0].bytes;
|
|
3571
|
+
const pausedKeys=fleetPausesByKey();
|
|
3572
|
+
for(const g of gs2){
|
|
3573
|
+
const n=night.get((g.machine||'')+'|'+g.harness);
|
|
3574
|
+
const row=document.createElement('div');
|
|
3575
|
+
row.className='cap-row2';
|
|
3576
|
+
const label=g.machine?g.machine+' · '+g.harness:g.harness;
|
|
3577
|
+
const paused=pausedKeys.has((g.machine||'')+'|'+g.harness);
|
|
3578
|
+
row.title=label+' — last '+wd+' days: '+fmt(g.sessions)+' sessions · '+mbFmt(g.bytes)+' posted'
|
|
3579
|
+
+(g.held>0?' · '+g.held+' held in window':'')
|
|
3580
|
+
+(g.retried>0?' · '+g.retried+' retried in window':'')
|
|
3581
|
+
+(n?' · '+nightLabel+': '+n.sessions+' sess'+(n.held>0?' ('+n.held+' held)':''):'')
|
|
3582
|
+
+(paused?' · CAPTURE PAUSED (posts are skipped — sessions are not backfilled)':'')
|
|
3583
|
+
+':\n'+g.agents.join('\n');
|
|
3584
|
+
row.innerHTML='<span class="cap-dot"></span>'+
|
|
3585
|
+
'<span class="hn" title="'+esc(label)+'">'+esc(label)+'</span>'+
|
|
3586
|
+
'<span class="cap-bar"><span class="fill" style="width:'+(g.bytes/Math.max(maxB,1)*100)+'%"></span></span>'+
|
|
3587
|
+
'<span class="st"><b>'+fmt(g.sessions)+'</b> sess · '+mbFmt(g.bytes)
|
|
3588
|
+
+(paused?'<span class="cap-paused" title="capture paused — posts are skipped (200) and sessions are not backfilled">PAUSED</span>':'')
|
|
3589
|
+
+(n&&n.held>0?'<span class="cap-held" title="'+nightLabel+' — failed posts the client will retry — cursor held">⚠ '+n.held+' held</span>':'')
|
|
3590
|
+
+'</span>'
|
|
3591
|
+
/* the newest-night sub-line — a full-basis flex child, so it wraps
|
|
3592
|
+
UNDER the primary line (the suffix never fits beside it; see the
|
|
3593
|
+
.cap-ton rule) and only exists for bundles with newest-night rows */
|
|
3594
|
+
+(n?'<span class="cap-ton" title="newest capture night ('+nightLabel+') — '+n.sessions+' session'+(n.sessions===1?'':'s')+' posted that night">'+nightLabel+': '+n.sessions+' sess</span>':'');
|
|
3595
|
+
host.appendChild(row);
|
|
3596
|
+
}
|
|
3597
|
+
return;
|
|
3598
|
+
}
|
|
3346
3599
|
if(ch&&Array.isArray(ch.rows)&&ch.rows.length){
|
|
3347
3600
|
/* phase-2 live view: the /distill accounting rows, grouped machine ×
|
|
3348
3601
|
harness — width encodes capture volume (each bundle's byte share of
|
|
@@ -3421,7 +3674,9 @@ function buildCapture(){
|
|
|
3421
3674
|
function lastNightByAgent(){
|
|
3422
3675
|
for(let i=NIGHTS.length-1;i>=0;i--){
|
|
3423
3676
|
const n=NIGHTS[i];
|
|
3424
|
-
|
|
3677
|
+
/* #452: the truth-management events count too — a superseded-only night
|
|
3678
|
+
is a real night */
|
|
3679
|
+
if(n.added.length+n.enriched.length+n.merged.length+n.linked.length+n.superseded.length+n.retracted.length+n.rewritten.length>0) return {date:n.date,by:n.by_agent};
|
|
3425
3680
|
}
|
|
3426
3681
|
return null;
|
|
3427
3682
|
}
|
|
@@ -3659,7 +3914,7 @@ function buildAgents(){
|
|
|
3659
3914
|
* counts — the paused state and dots re-derive from the fresh payload. */
|
|
3660
3915
|
async function refreshFleet(){
|
|
3661
3916
|
try{
|
|
3662
|
-
const dd=await api('/dashboard/data?range=
|
|
3917
|
+
const dd=await api('/dashboard/data?range='+RANGE+'d');
|
|
3663
3918
|
D=dd;
|
|
3664
3919
|
buildAgents();
|
|
3665
3920
|
buildCapture();
|
|
@@ -4040,7 +4295,7 @@ let refreshInFlight=false;
|
|
|
4040
4295
|
async function refreshAfterWrite(){
|
|
4041
4296
|
if(refreshInFlight) return; refreshInFlight=true;
|
|
4042
4297
|
try{
|
|
4043
|
-
const [ff,dd]=await Promise.all([api('/dashboard/field'),api('/dashboard/data?range=
|
|
4298
|
+
const [ff,dd]=await Promise.all([api('/dashboard/field'),api('/dashboard/data?range='+RANGE+'d').catch(()=>null)]);
|
|
4044
4299
|
if(ff){ F=ff; buildWorld(); }
|
|
4045
4300
|
if(dd){ D=dd; }
|
|
4046
4301
|
renderBand(); buildRoll(); buildCapture(); renderLegend(); renderFeel();
|
|
@@ -4351,9 +4606,9 @@ async function pollHealth(){
|
|
|
4351
4606
|
===================================================================== */
|
|
4352
4607
|
async function boot(){
|
|
4353
4608
|
const [dd,ff,ee,hh,hd,mm]=await Promise.allSettled([
|
|
4354
|
-
api('/dashboard/data?range=
|
|
4609
|
+
api('/dashboard/data?range='+RANGE+'d'),
|
|
4355
4610
|
api('/dashboard/field'),
|
|
4356
|
-
api('/dashboard/events?days=
|
|
4611
|
+
api('/dashboard/events?days='+RANGE),
|
|
4357
4612
|
fetch('/health'),
|
|
4358
4613
|
api('/health/detail'),
|
|
4359
4614
|
api('/dashboard/model'),
|
package/dist/calibration.d.ts
CHANGED
|
@@ -28,6 +28,36 @@
|
|
|
28
28
|
* ranking. Long-term remembering is the product; time preference stays mild.
|
|
29
29
|
*/
|
|
30
30
|
export declare const DECAY_HALF_LIFE_DAYS = 365;
|
|
31
|
+
/**
|
|
32
|
+
* Per-use strength promotion rate (#448, the strength model's upward path):
|
|
33
|
+
* one application of `promotion = PROMOTION_RATE × (1 − S / IMPORTANCE_CEILING)
|
|
34
|
+
* × S^(−0.3)` per new use (S = stored base_strength), iterated once per
|
|
35
|
+
* access-count delta by the nightly stagePromotion stage and clamped at the
|
|
36
|
+
* ceiling. Calibrated so the first access on a median-0.35 memory gives ~+0.03
|
|
37
|
+
* (0.35 → 0.3786). Shape grounded in LTP saturation curves (Cao & Harris
|
|
38
|
+
* 2014), the power law of practice (α ≈ 0.3, Newell & Rosenbloom 1981), and
|
|
39
|
+
* spaced-repetition stability multipliers (S^−0.53, SuperMemo SM-17,
|
|
40
|
+
* n > 60,000 real repetitions): bigger boosts for weaker memories,
|
|
41
|
+
* saturating near the ceiling, never fully stopping (asymptotic). The decay
|
|
42
|
+
* side is untouched — a memory's fade RATE no longer depends on its use or
|
|
43
|
+
* link history (the #448 hardening removal); use raises the STORED score
|
|
44
|
+
* instead, and abandonment fades it on the same 365-day clock as everything
|
|
45
|
+
* else. RELEASE-MANAGED per this module's evolution contract — no config key.
|
|
46
|
+
*/
|
|
47
|
+
export declare const PROMOTION_RATE = 0.033;
|
|
48
|
+
/**
|
|
49
|
+
* Lower bound on the promotion formula's S input (#453 review Minor finding,
|
|
50
|
+
* owner decision 2026-09-16). base_strength 0.0 is a writable value (legacy
|
|
51
|
+
* rows, operator edits), and S^(−0.3) has a zero-singularity: unfloored, one
|
|
52
|
+
* access on a 0.0 row computed delta = 0.033 × (1 − 0/0.95) × 0^−0.3 =
|
|
53
|
+
* Infinity, and the ceiling clamp turned that into a leap straight to 0.95.
|
|
54
|
+
* Floored at 0.01, the same access lands at 0.033 × (1 − 0.01/0.95) ×
|
|
55
|
+
* 0.01^(−0.3) ≈ +0.13 (0.0 → ≈0.14) — the biggest first boost the formula
|
|
56
|
+
* can give, bounded and far from the ceiling. The floor clamps the FORMULA
|
|
57
|
+
* INPUT only: storage keeps a 0.0 row at 0.0 until it is used, and the
|
|
58
|
+
* promoted result is nonzero from then on. RELEASE-MANAGED like PROMOTION_RATE.
|
|
59
|
+
*/
|
|
60
|
+
export declare const PROMOTION_STRENGTH_FLOOR = 0.01;
|
|
31
61
|
/** Default k for retrieve() (/search without an explicit limit). #192. */
|
|
32
62
|
export declare const SEARCH_LIMIT = 8;
|
|
33
63
|
/** Default k for searchRecent() (/recent without an explicit limit). #192. */
|
|
@@ -85,23 +115,58 @@ export declare const SCORE_SIMILARITY_WEIGHT = 0.5;
|
|
|
85
115
|
export declare const SCORE_STRENGTH_WEIGHT = 0.2;
|
|
86
116
|
/** Graph-centrality share of the composite score (was 0.20; see above). */
|
|
87
117
|
export declare const SCORE_CONNECTIONS_WEIGHT = 0.15;
|
|
88
|
-
/**
|
|
118
|
+
/**
|
|
119
|
+
* Log-saturation degree K for the connections term (#449 PR E, items 1+3):
|
|
120
|
+
* the credit is min(1, log1p(k) / log1p(K)) × SCORE_CONNECTIONS_WEIGHT on
|
|
121
|
+
* the ABSOLUTE undirected-degree scale k — full at k = K, exactly +0 at
|
|
122
|
+
* k = 0 (unlinked rows score bit-identically to the linear term they
|
|
123
|
+
* replace). Provenance: p99 of the REAL undirected degree distribution
|
|
124
|
+
* (read-only audit 2026-09-17 of the canonical wave snapshot, 18,798
|
|
125
|
+
* memories / 14,600 linked: p50 = 4, p90 = 8, p95 = 9, p99 = 15, p99.9 =
|
|
126
|
+
* 28.4, max = 40; 135 memories ≥ 16 links = 0.92% of linked) — so the top
|
|
127
|
+
* ~1% of hubs tie at full credit and everything below differentiates
|
|
128
|
+
* modestly. Shape grounded in the #449 research base: ACT-R fan saturation
|
|
129
|
+
* (Anderson & Reder 1999 — activation falls with the LOG of fan), SAM's
|
|
130
|
+
* saturating returns, cue overload (Watkins & Watkins 1975). Replaces the
|
|
131
|
+
* candidate-set-relative normalization #449 deleted (k / the per-query max
|
|
132
|
+
* degree) — a row's score no longer depends on which other rows happened
|
|
133
|
+
* to match. Alternatives K = 8 (p90 — collapses all p90+ rows to
|
|
134
|
+
* full credit) and K = 32 (≈p99.9 — leaves the p99 hub at 0.12 of the
|
|
135
|
+
* term) were tabled in the #449 spec. RELEASE-MANAGED per this module's
|
|
136
|
+
* evolution contract — overridable only through the configureScoring seam.
|
|
137
|
+
*/
|
|
138
|
+
export declare const CONNECTIONS_SATURATION_DEGREE = 16;
|
|
139
|
+
/** Time-curve share of the composite score — the merged curve's blend weight
|
|
140
|
+
* AND slow-region amplitude (was 0.10; see above). The four blend weights
|
|
141
|
+
* still sum to 1.0; the head amplitude below is a documented overshoot, NOT
|
|
142
|
+
* a fifth blend weight (#430). Zero (via the seam) disables the whole term. */
|
|
89
143
|
export declare const SCORE_RECENCY_WEIGHT = 0.15;
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
144
|
+
/** Hourly decay of the merged curve's slow region (#430) — promoted from the
|
|
145
|
+
* inline literal computeScore carried; half-life ≈ 57.7 days. At and beyond
|
|
146
|
+
* RECENCY_HEAD_DAYS the curve is bit-identical to the pre-#430 slow term
|
|
147
|
+
* (SCORE_RECENCY_WEIGHT × RECENCY_HOURLY_DECAY^hours). */
|
|
148
|
+
export declare const RECENCY_HOURLY_DECAY = 0.9995;
|
|
149
|
+
/** Merged time-curve head amplitude at age 0 (#430) — the pre-#430 slow
|
|
150
|
+
* weight 0.15 + fresh bonus 0.15: the freshness job is now the early steep
|
|
151
|
+
* part of ONE curve. The head joins value-continuously onto the slow region
|
|
152
|
+
* at RECENCY_HEAD_DAYS; its rate is DERIVED from that continuity, never a
|
|
153
|
+
* free constant. */
|
|
154
|
+
export declare const RECENCY_HEAD_WEIGHT = 0.3;
|
|
155
|
+
/** Join age in days of the merged time curve (#430) — the head window edge,
|
|
156
|
+
* carried over from the pre-#430 freshness window. Nightly capture means
|
|
157
|
+
* 1 day is the floor of "fresh" (#191 Phase B). */
|
|
158
|
+
export declare const RECENCY_HEAD_DAYS = 7;
|
|
96
159
|
/** Score multiplier for a memory a later decision superseded (0.15.2; the
|
|
97
160
|
* belief walk (#393 D) is the primary mechanism — this is the safety net
|
|
98
161
|
* for rows the walk does not reach). */
|
|
99
162
|
export declare const SUPERSEDED_DEMOTION = 0.5;
|
|
100
|
-
/** #
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
163
|
+
/** #430 merged scope affinity — ONE term replacing #203's two boosts (the
|
|
164
|
+
* project-affinity and domain-affinity constants) at their shared value.
|
|
165
|
+
* Boost = max(project-match indicator (1 on exact match), max overlapping
|
|
166
|
+
* domain-tag weight) × this weight: the strongest single scope signal counts
|
|
167
|
+
* once, never stacked. ADDITIVE, zero-boost neutral, never a penalty — an
|
|
168
|
+
* absent scope adds exactly 0; a foreign memory ranks equal, not lower. */
|
|
169
|
+
export declare const SCOPE_AFFINITY_WEIGHT = 0.15;
|
|
105
170
|
/** #205 RRF k parameter (1/(k+rank+1)) — matches the pre-#205 hardcoded 60
|
|
106
171
|
* so the no-config path was byte-identical to 0.15.3. */
|
|
107
172
|
export declare const RRF_K = 60;
|