instar 1.3.1643 → 1.3.1645

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.
Files changed (46) hide show
  1. package/dashboard/console/accounts.js +3 -3
  2. package/dashboard/console/agents.js +2 -2
  3. package/dashboard/console/allowed.js +3 -4
  4. package/dashboard/console/console.js +8 -3
  5. package/dashboard/console/costs.js +2 -2
  6. package/dashboard/console/development.js +3 -3
  7. package/dashboard/console/ideas.js +3 -3
  8. package/dashboard/console/inside.js +4 -9
  9. package/dashboard/console/learned.js +2 -2
  10. package/dashboard/console/machines.js +2 -2
  11. package/dashboard/console/mandates.js +2 -2
  12. package/dashboard/console/maturation-board.js +6 -7
  13. package/dashboard/console/messages.js +2 -2
  14. package/dashboard/console/plans.js +3 -6
  15. package/dashboard/console/promises.js +3 -6
  16. package/dashboard/console/schedule.js +3 -6
  17. package/dashboard/console/secrets.js +2 -2
  18. package/dashboard/console/ui.js +104 -1
  19. package/dashboard/index.html +3 -1
  20. package/dist/data/standards-guard-index.json +1 -1
  21. package/dist/data/standards-guard-index.meta.json +2 -2
  22. package/dist/data/standards-registry.meta.json +1 -1
  23. package/dist/feedback-factory/execute/FeedbackExecutorService.js +2 -2
  24. package/dist/feedback-factory/execute/FeedbackExecutorService.js.map +1 -1
  25. package/dist/feedback-factory/execute/confinementCanary.js +1 -1
  26. package/dist/feedback-factory/execute/confinementCanary.js.map +1 -1
  27. package/dist/feedback-factory/execute/executePolicy.d.ts +2 -2
  28. package/dist/feedback-factory/execute/executePolicy.d.ts.map +1 -1
  29. package/dist/feedback-factory/execute/executePolicy.js +3 -2
  30. package/dist/feedback-factory/execute/executePolicy.js.map +1 -1
  31. package/dist/native/InstarWedgeCanary.app/Contents/MacOS/InstarWedgeCanary +0 -0
  32. package/dist/native/instar-desktop-host +0 -0
  33. package/package.json +1 -1
  34. package/scripts/locked-proof/lib.mjs +6 -6
  35. package/src/data/builtin-manifest.json +2 -2
  36. package/src/data/standards-guard-index.json +1 -1
  37. package/src/data/standards-guard-index.meta.json +2 -2
  38. package/src/data/standards-registry.meta.json +1 -1
  39. package/upgrades/1.3.1644.md +20 -0
  40. package/upgrades/1.3.1645.md +30 -0
  41. package/upgrades/side-effects/console-refresh-keeps-place.eli16.md +13 -0
  42. package/upgrades/side-effects/console-refresh-keeps-place.md +78 -0
  43. package/upgrades/side-effects/feedback-confined-vitest-direct.md +67 -0
  44. package/upgrades/side-effects/feedback-origin-test-cache.md +66 -0
  45. package/upgrades/side-effects/locked-proof-neutral-wording.eli16.md +5 -0
  46. package/upgrades/side-effects/locked-proof-neutral-wording.md +9 -0
@@ -7,7 +7,7 @@
7
7
  // repair / retry / cancel POST /subscription-relogin/repair-cell operator session
8
8
 
9
9
  import { buildAccountsPage, frameworkName } from './accounts-model.js';
10
- import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell, askPin, forgetOperatorSession } from './ui.js';
10
+ import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell, askPin, forgetOperatorSession, firstPaint, arrivalScroll } from './ui.js';
11
11
  import { legacyHref } from './nav-model.js';
12
12
  import { verifiedDisplayName } from './display-identity.js';
13
13
 
@@ -22,7 +22,7 @@ async function postJson(path, body, headers = {}) {
22
22
 
23
23
  export async function renderAccounts(main, ctx) {
24
24
  const { get } = ctx;
25
- if (!main.querySelector('.acct-list')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Capacity · Accounts' }), el('h1', { class: 'page-title skeleton', text: 'Loading accounts' })));
25
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Capacity · Accounts' }), el('h1', { class: 'page-title skeleton', text: 'Loading accounts' })));
26
26
  const [pool, relogin, inUse, machines] = await Promise.all([get('/subscription-pool?scope=pool', 8000), get('/subscription-relogin?scope=pool', 8000), get('/subscription-pool/in-use'), get('/pool')]);
27
27
  const names = machines.ok && Array.isArray(machines.data && machines.data.machines) ? Object.fromEntries(machines.data.machines.map((m) => [m.machineId, m.nickname])) : {};
28
28
  const episodes = relogin.ok && Array.isArray(relogin.data && relogin.data.episodes) ? relogin.data.episodes : [];
@@ -66,7 +66,7 @@ export async function renderAccounts(main, ctx) {
66
66
  !relogin.ok ? note(`Couldn't read sign-in repairs: ${relogin.reason}.`) : null,
67
67
  needs, list, legacyLink());
68
68
  const want = new URLSearchParams((location.hash.split('?')[1] || '')).get('s');
69
- if (want === 'needs' && needs) needs.scrollIntoView({ block: 'start' });
69
+ if (want === 'needs') arrivalScroll(needs);
70
70
  }
71
71
 
72
72
  function legacyLink() {
@@ -14,7 +14,7 @@
14
14
  import {
15
15
  buildAgentsPage, threadModel, inviteResultWords, pinResultWords, bearerResultWords, enableWords, bindRequest, bindResultWords, localTime,
16
16
  } from './agents-model.js';
17
- import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin } from './ui.js';
17
+ import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint } from './ui.js';
18
18
  import { legacyHref } from './nav-model.js';
19
19
  import { verifiedDisplayName, uniqueNamedChoices } from './display-identity.js';
20
20
  import { topicNameMap } from './topic-names.js';
@@ -48,7 +48,7 @@ async function send(path, body, method = 'POST') {
48
48
  }
49
49
 
50
50
  export async function renderAgents(main, ctx) {
51
- if (!main.querySelector('.agents-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading other agents' })));
51
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading other agents' })));
52
52
  const [health, pairingRes, invitesRes, peersRes, convRes, threadsRes, poolRes, topicsRes] = await Promise.all([
53
53
  read('/threadline/health'), read('/threadline/pairing'), read('/threadline/pairing/invites'),
54
54
  read('/threadline/peers/health?scope=pool', POOL_TIMEOUT_MS), read('/threadline/conversations?scope=mesh'), read('/threadline/observability/threads'), read('/pool'), read('/telegram/topics?scope=pool', POOL_TIMEOUT_MS),
@@ -8,7 +8,7 @@ import {
8
8
  buildProfile, buildHistory, buildGuards, buildSuggestions, buildFeatures, flattenFeature, profileChanges,
9
9
  walkToEnabled, canTurnOff, canUndoOff, needsConsent, facts, applyText, refusalText, PROFILE_LABEL, PROFILE_SENTENCE, STATE_WORD,
10
10
  } from './allowed-model.js';
11
- import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, actionButton, askPin, operatorSession, forgetOperatorSession } from './ui.js';
11
+ import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, actionButton, askPin, operatorSession, forgetOperatorSession, firstPaint, arrivalScroll } from './ui.js';
12
12
  import { legacyHref } from './nav-model.js';
13
13
 
14
14
  const featureErrors = new Map(); // feature id → the server's last refusal, shown beside the row
@@ -40,7 +40,7 @@ const errWords = (r) => {
40
40
 
41
41
  export async function renderAllowed(main, ctx) {
42
42
  const { get, post } = ctx;
43
- if (!main.querySelector('.allowed-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Settings · What I’m allowed to do' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
43
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Settings · What I’m allowed to do' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
44
44
  const [auto, hist, elev, guardsRes, featRes] = await Promise.all([
45
45
  get('/autonomy'), get('/autonomy/history'), get('/autonomy/elevation'), get('/guards?scope=pool', 8000), get('/features'),
46
46
  ]);
@@ -66,8 +66,7 @@ export async function renderAllowed(main, ctx) {
66
66
 
67
67
  const want = new URLSearchParams((location.hash.split('?')[1] || '')).get('s');
68
68
  if (want) {
69
- const sec = main.querySelector(`#sec-${want}`);
70
- if (sec) sec.scrollIntoView({ block: 'start' });
69
+ arrivalScroll(main.querySelector(`#sec-${want}`));
71
70
  }
72
71
  }
73
72
 
@@ -30,7 +30,7 @@ import { renderSessions } from './sessions.js';
30
30
  import { renderMaturation, maturationTile } from './maturation-board.js';
31
31
  import { renderIdeas, ideasTile } from './ideas.js';
32
32
  import { paceCard } from './pace.js';
33
- import { sheetIsOpen, closeSheet, unlockCheck, machineIssue, MACHINES_PAGE } from './ui.js';
33
+ import { sheetIsOpen, closeSheet, unlockCheck, machineIssue, MACHINES_PAGE, newArrival, refreshHeld } from './ui.js';
34
34
 
35
35
  const TOKEN_KEY = 'instar_token'; // shared with the legacy dashboard
36
36
  const THEME_KEY = 'instar_console_theme';
@@ -639,6 +639,7 @@ const NATIVE = {
639
639
  function route() {
640
640
  if (homeRun) { homeRun.active = false; for (const p of Object.values(homeRun.pending)) if (p && p !== true) clearTimeout(p); homeRun = null; }
641
641
  closeSheet();
642
+ newArrival();
642
643
  const path = (location.hash.replace(/^#\/?/, '') || 'home').split('?')[0];
643
644
  const id = path.split('/')[0];
644
645
  const group = GROUPS.find((g) => g.id === id);
@@ -647,9 +648,13 @@ function route() {
647
648
  if (refreshTimer) { clearInterval(refreshTimer); refreshTimer = null; }
648
649
  if (NATIVE[path]) {
649
650
  NATIVE[path](main, { get, post, getCommitments });
650
- // Pages refresh in place every 30 s while visible, never over an open sheet.
651
+ // Pages refresh in place every 30 s while visible, never over an open sheet or an
652
+ // interaction in progress, and one at a time: a slow read cannot paint over a newer one.
653
+ let refreshing = false;
651
654
  refreshTimer = setInterval(() => {
652
- if (document.visibilityState === 'visible' && !sheetIsOpen() && (location.hash.replace(/^#\/?/, '').split('?')[0]) === path) NATIVE[path](main, { get, post, getCommitments });
655
+ if (refreshing || document.visibilityState !== 'visible' || sheetIsOpen() || refreshHeld(main) || (location.hash.replace(/^#\/?/, '').split('?')[0]) !== path) return;
656
+ refreshing = true;
657
+ Promise.resolve().then(() => NATIVE[path](main, { get, post, getCommitments })).catch(() => { /* @silent-fallback-ok: a failed refresh keeps the page on screen; the next tick tries again */ }).finally(() => { refreshing = false; });
653
658
  }, REFRESH_MS);
654
659
  } else if (group) renderGroup(main, group); else renderHome(main);
655
660
  window.scrollTo(0, 0);
@@ -4,7 +4,7 @@
4
4
  // reaches the DOM through textContent; the model (costs-model.js) decides everything.
5
5
 
6
6
  import { buildCostsPage, WINDOWS, DEFAULT_WINDOW, GRAINS, windowParams, spendQuery, committedLine, basisWord, fmtTokens, fmtUsd, modelWord, doorWord } from './costs-model.js';
7
- import { el, setKids, pageHead, section, sheet, story, details, note, cantTell } from './ui.js';
7
+ import { el, setKids, pageHead, section, sheet, story, details, note, cantTell, firstPaint } from './ui.js';
8
8
  import { legacyHref } from './nav-model.js';
9
9
  import { sessionLabel } from './home-model.js';
10
10
 
@@ -19,7 +19,7 @@ const view = { window: DEFAULT_WINDOW, grain: 'day' };
19
19
 
20
20
  export async function renderCosts(main, ctx) {
21
21
  const { get } = ctx;
22
- if (!main.querySelector('.costs-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading what thinking costs' })));
22
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading what thinking costs' })));
23
23
  const w = windowParams(view.window);
24
24
  const [spend, caps, status, metrics, insights, tokens, claude, codex, orphans, chains, sessions] = await Promise.all([
25
25
  get(`/routing-spend/summary?${spendQuery(view.grain, w.sinceHours)}`), get('/routing-spend/caps'), get('/routing-spend/enable-status'),
@@ -13,7 +13,7 @@ import {
13
13
  buildShipping, buildPace, buildFeedback, developmentHeadline, stoppedLead, foldFromHash, maintainerMode, authorityCard, holdWords, verifyWords, rollbackWords,
14
14
  readinessAuthorityRequest, triageAuthorityRequest, commitRequest, authorityResultWords, commitResultWords, newDecisionRef, PACE_METRICS, DEV_TOOLS_KEY,
15
15
  } from './development-model.js';
16
- import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin } from './ui.js';
16
+ import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint, arrivalScroll } from './ui.js';
17
17
  import { legacyHref } from './nav-model.js';
18
18
 
19
19
  const EYEBROW = 'Work · Development';
@@ -93,7 +93,7 @@ export async function renderDevelopment(main, ctx) {
93
93
  // Each navigation gets a new <main>, so no page yet means someone just arrived: a `?s=` link wins over the
94
94
  // remembered fold then. A 30-second refresh keeps the page, so it never overrides what the operator opened or closed.
95
95
  if (!main.querySelector('.dev-page')) {
96
- setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading development' })));
96
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading development' })));
97
97
  const want = foldFromHash(location.hash);
98
98
  if (want) { view.open = want; view.scrollTo = want; }
99
99
  }
@@ -140,7 +140,7 @@ function paint() {
140
140
  if (view.scrollTo) {
141
141
  const sec = main.querySelector(`#sec-${view.scrollTo}`);
142
142
  view.scrollTo = null;
143
- if (sec) sec.scrollIntoView({ block: 'start' });
143
+ arrivalScroll(sec);
144
144
  }
145
145
  }
146
146
 
@@ -1,7 +1,7 @@
1
1
  // Ecosystem ideas page (docs/specs/ecosystem-idea-pipeline.md §9). Advice only: it builds nothing.
2
2
  // Every value is written with textContent via el(). Decisions go through the PIN plan and commit.
3
3
 
4
- import { el, pageHead, cantTell, askPin, note } from './ui.js';
4
+ import { el, pageHead, cantTell, askPin, note, setKids } from './ui.js';
5
5
  import { BANDS, ideasInBand, headline, funnelLine, admissionLine, classWords, projectLine, planRequest } from './ideas-model.js';
6
6
 
7
7
  let probeResult = null;
@@ -50,7 +50,7 @@ export async function renderIdeas(main, api) {
50
50
  const refresh = () => renderIdeas(main, api);
51
51
  const r = await api.get('/ecosystem-ideas?scope=pool', 12_000);
52
52
  if (!r.ok) {
53
- main.replaceChildren(r.reason === 'switched off on this agent'
53
+ setKids(main, r.reason === 'switched off on this agent'
54
54
  ? el('section', {}, el('div', { class: 'eyebrow', text: 'Ideas' }), el('h1', { text: 'The idea review is not switched on for this agent.' }))
55
55
  : cantTell(r.reason));
56
56
  return;
@@ -84,7 +84,7 @@ export async function renderIdeas(main, api) {
84
84
  parts.push(el('div', { class: 'need-card' }, el('span', { class: 'need-title', text: `Suggested: ${p.repo}` }), el('p', { class: 'need-sub', text: p.reason }), add, status));
85
85
  }
86
86
  if (s.lastTickError) parts.push(note(`The last round had a problem: ${s.lastTickError}`));
87
- main.replaceChildren(el('div', { class: 'ideas-page' }, parts));
87
+ setKids(main, el('div', { class: 'ideas-page' }, parts));
88
88
  }
89
89
 
90
90
  export function ideasTile(get) {
@@ -16,7 +16,7 @@ import {
16
16
  CHIPS, KINDS, KIND_WORDS, COUNTERPARTIES, COUNTERPARTY_WORDS, STAGE_INFO, LOOKUP_KINDS, SCOPES, ADVISORY, CLAIM_KEYS, REVIEW_KEYS, CLAIMS_PAGE, WINDOW_STEP,
17
17
  setPeerNames,
18
18
  } from './inside-model.js';
19
- import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell } from './ui.js';
19
+ import { el, setKids, pageHead, sheet, closeSheet, story, details, note, cantTell, firstPaint, arrivalScroll } from './ui.js';
20
20
 
21
21
  const EYEBROW = 'Settings · Under the hood';
22
22
  const SUBTITLE = 'Conversation history, recurring problems, checks on my claims.';
@@ -24,7 +24,6 @@ const SUBTITLE = 'Conversation history, recurring problems, checks on my claims.
24
24
  const view = { chip: 'all', kind: null, counterparty: null, limit: WINDOW_STEP, older: [], claimsLimit: CLAIMS_PAGE, open: new Set() };
25
25
  let lastProblems = null;
26
26
  let lookupNode = null;
27
- let scrolledFor = null;
28
27
 
29
28
  function bearer() { try { return localStorage.getItem('instar_token') || ''; } catch { return ''; } }
30
29
  /** A write that keeps the status and body (the revoke route needs the intent header). */
@@ -38,7 +37,7 @@ async function send(req) {
38
37
 
39
38
  export async function renderInside(main, ctx) {
40
39
  const { get } = ctx;
41
- if (!main.querySelector('.inside-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
40
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
42
41
  const [recent, stats, sessions, pool, insights, week, all, claimsRes, reviewsRes, pairingRes, ...pages] = await Promise.all([
43
42
  get(recentUrl({ limit: view.limit, counterparty: view.counterparty })), get('/shared-state/stats'), get('/shared-state/sessions'), get('/pool'),
44
43
  get('/failures/insights?limit=50'), get('/failures/analysis?sinceDays=7'), get('/failures/analysis'),
@@ -65,14 +64,10 @@ export async function renderInside(main, ctx) {
65
64
  pageHead({ eyebrow: `${EYEBROW} · How I’m doing inside`, headline: h.text, tone: h.tone, sub: h.sub || SUBTITLE, facts: insideFacts({ activity, problems, claims, reviews }) }),
66
65
  el('div', { class: 'page-sections' }, sections)));
67
66
  const want = new URLSearchParams(location.hash.split('?')[1] || '').get('s');
68
- if (want && scrolledFor !== want) { scrolledFor = want; const sec = main.querySelector(`#sec-${want}`); if (sec) { openFold(sec); sec.scrollIntoView({ block: 'start' }); } }
67
+ if (want) arrivalScroll(main.querySelector(`#sec-${want}`), { open: true });
69
68
  }
70
69
 
71
70
  // ── shared bits ───────────────────────────────────────────────────────────
72
- function openFold(sec) {
73
- const head = sec.querySelector(':scope > button.list-head');
74
- if (head && head.getAttribute('aria-expanded') === 'false') head.click();
75
- }
76
71
  /** A collapsible block whose open state survives refreshes. */
77
72
  function fold(id, title, count, body, { tone } = {}) {
78
73
  const isOpen = view.open.has(id);
@@ -291,5 +286,5 @@ function lookupBlock(get) {
291
286
  /** Test seam: forget the remembered view between tests. */
292
287
  export function _resetInsideState() {
293
288
  view.chip = 'all'; view.kind = null; view.counterparty = null; view.limit = WINDOW_STEP; view.older = []; view.claimsLimit = CLAIMS_PAGE; view.open = new Set();
294
- lastProblems = null; lookupNode = null; scrolledFor = null;
289
+ lastProblems = null; lookupNode = null;
295
290
  }
@@ -9,7 +9,7 @@ import {
9
9
  buildPreferences, buildCorrections, buildConflicts, machineNames, learnedHeadline, learnedFacts,
10
10
  correctionsUrl, kindWords, confidenceWords, correctionStatusWords, ADVISORY_LINE,
11
11
  } from './learned-model.js';
12
- import { el, setKids, pageHead, section, sheet, story, details, note, cantTell } from './ui.js';
12
+ import { el, setKids, pageHead, section, sheet, story, details, note, cantTell, firstPaint } from './ui.js';
13
13
 
14
14
  const PAGE = 20;
15
15
  // The last good preferences build, so a failed refresh shows it with its age (stale) instead of nothing.
@@ -21,7 +21,7 @@ const CORRECTION_KEYS = ['id', 'kind', 'status', 'occurrenceCount', 'detectedAt'
21
21
 
22
22
  export async function renderLearned(main, ctx) {
23
23
  const { get } = ctx;
24
- if (!main.querySelector('.learned-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Settings · What I’ve learned about you' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
24
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Settings · What I’ve learned about you' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
25
25
  const [prefsRes, firstPage, conflictsRes, poolRes] = await Promise.all([
26
26
  get('/preferences/session-context'), get(correctionsUrl(null, PAGE)), get('/state/conflicts'), get('/pool'),
27
27
  ]);
@@ -3,14 +3,14 @@
3
3
  // the legacy tab until their PIN sheet is rebuilt (the plan's first cut).
4
4
 
5
5
  import { buildMachinesPage, ropesFor, healthOf, usageOf, machineCarePool } from './machines-model.js';
6
- import { el, setKids, pageHead, sheet, closeSheet, story, note, cantTell } from './ui.js';
6
+ import { el, setKids, pageHead, sheet, closeSheet, story, note, cantTell, firstPaint } from './ui.js';
7
7
  import { legacyHref } from './nav-model.js';
8
8
 
9
9
  const STATE_DOT = { ok: 'dot live', attn: 'dot attn', bad: 'dot bad', stale: 'dot unknown', unknown: 'dot unknown' };
10
10
 
11
11
  export async function renderMachines(main, ctx) {
12
12
  const { get } = ctx;
13
- if (!main.querySelector('.machine-cards')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Capacity · Machines' }), el('h1', { class: 'page-title skeleton', text: 'Loading machines' })));
13
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Capacity · Machines' }), el('h1', { class: 'page-title skeleton', text: 'Loading machines' })));
14
14
  const [pool, health, guards, sessions, care] = await Promise.all([get('/pool'), get('/health'), get('/guards'), get('/sessions?scope=pool', 8000), get('/machine-maintenance?scope=pool', 8000)]);
15
15
  const live = sessions.ok && Array.isArray(sessions.data && sessions.data.sessions) ? sessions.data.sessions.filter((s) => s.status === 'running' || s.status === 'starting') : [];
16
16
  const byMachine = {};
@@ -9,7 +9,7 @@ import {
9
9
  issueRequest, revokeRequest, grantRequest, approveRequest, denyRequest, resultWords,
10
10
  FLOOR_ACTIONS, GRANT_DURATIONS, AUTHORITIES_TEMPLATE, humanAction, defaultExpiryLocal,
11
11
  } from './mandates-model.js';
12
- import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin } from './ui.js';
12
+ import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, askPin, firstPaint } from './ui.js';
13
13
  import { legacyHref } from './nav-model.js';
14
14
  import { verifiedDisplayName, uniqueNamedChoices } from './display-identity.js';
15
15
 
@@ -29,7 +29,7 @@ async function send(path, body) {
29
29
 
30
30
  export async function renderMandates(main, ctx) {
31
31
  const { get } = ctx;
32
- if (!main.querySelector('.slips-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'People · Permission slips' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
32
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'People · Permission slips' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
33
33
  const [slipsRes, auditRes, approvalsRes, usersRes, healthRes, tlHealth, pairing] = await Promise.all([
34
34
  get('/mandate'), get(`/mandate/audit?limit=${AUDIT_LIMIT}`), get('/authorization-requests?status=pending'),
35
35
  get('/permissions/users'), get('/health'), get('/threadline/health'), get('/threadline/pairing/invites'),
@@ -5,7 +5,7 @@
5
5
  // Layout, top to bottom: one answer sentence · a square per feature (the glance) ·
6
6
  // plain groups (Broken, Stuck, …) with one plain line per feature · details on tap.
7
7
 
8
- import { el, cantTell } from './ui.js';
8
+ import { el, cantTell, setKids, firstPaint, arrivalScroll } from './ui.js';
9
9
  import { maturationPaceNode } from './pace.js';
10
10
  import {
11
11
  GROUPS, groupOf, groupCounts, headline, subline, parseState, stateQuery, openGroups,
@@ -21,16 +21,16 @@ let lastBoard = null;
21
21
  * @param nav { read() → query string, write(query) } — where view state lives in the URL
22
22
  */
23
23
  export async function renderMaturation(main, api, nav = consoleNav()) {
24
- if (!lastBoard) main.replaceChildren(el('p', { class: 'mb-loading', text: 'Reading every machine…' }));
24
+ if (!lastBoard) firstPaint(main, el('p', { class: 'mb-loading', text: 'Reading every machine…' }));
25
25
  const r = await api.get('/maturation/board?scope=pool', POOL_TIMEOUT_MS);
26
26
  if (!r.ok) {
27
27
  if (r.reason === 'switched off on this agent') {
28
- main.replaceChildren(el('section', { class: 'mb-hero' },
28
+ setKids(main, el('section', { class: 'mb-hero' },
29
29
  el('div', { class: 'eyebrow', text: 'Feature maturation' }),
30
30
  el('h1', { class: 'mb-answer', text: 'The maturation board is not switched on for this agent.' })));
31
31
  return;
32
32
  }
33
- if (!lastBoard) { main.replaceChildren(cantTell(r.reason)); return; }
33
+ if (!lastBoard) { setKids(main, cantTell(r.reason)); return; }
34
34
  } else {
35
35
  lastBoard = r.data;
36
36
  }
@@ -111,13 +111,12 @@ function draw(main, board, nav, pace = null) {
111
111
  return el('section', { class: 'mb-group' }, head, body);
112
112
  }).filter(Boolean);
113
113
 
114
- main.replaceChildren(hero, glance, tools,
114
+ setKids(main, hero, glance, tools,
115
115
  sections.length ? el('div', { class: 'mb-groups' }, sections) : el('p', { class: 'mb-sub', text: 'Nothing matches.' }),
116
116
  sourcesNote(board));
117
117
 
118
118
  if (st.f) {
119
- const target = main.querySelector('.mb-row[aria-expanded="true"]');
120
- if (target && typeof target.scrollIntoView === 'function') target.scrollIntoView({ block: 'center' });
119
+ arrivalScroll(main.querySelector('.mb-row[aria-expanded="true"]'), { key: `row:${st.f}`, block: 'center' });
121
120
  }
122
121
  }
123
122
 
@@ -3,7 +3,7 @@
3
3
  // comes from /health and needs none.
4
4
 
5
5
  import { buildMessagesPage, displayModel, heldHeadline } from './messages-model.js';
6
- import { el, setKids, pageHead, section, sheet, closeSheet, story, note, cantTell, askPin, operatorSession, forgetOperatorSession } from './ui.js';
6
+ import { el, setKids, pageHead, section, sheet, closeSheet, story, note, cantTell, askPin, operatorSession, forgetOperatorSession, firstPaint } from './ui.js';
7
7
  import { legacyHref } from './nav-model.js';
8
8
  import { verifiedDisplayName, uniqueNamedChoices } from './display-identity.js';
9
9
  import { topicNameMap, isRealTopicName } from './topic-names.js';
@@ -25,7 +25,7 @@ async function opFetch(path, opts = {}) {
25
25
  export async function renderMessages(main, ctx) {
26
26
  const { get } = ctx;
27
27
  const first = !main.querySelector('.msg-page');
28
- if (first) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'People · Messages to you' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
28
+ if (first) firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'People · Messages to you' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
29
29
  // A locked page is not re-polled against the PIN-gated routes every 30 s: only the
30
30
  // first render (and a render after an unlock) asks for the records.
31
31
  const locked = !first && !operatorSession() && main.querySelector('.need-card');
@@ -7,7 +7,7 @@
7
7
  import {
8
8
  buildPlansPage, roundsTrack, projectActions, resultWords, refusalWords, driftSentence, linkChips, stepList, blockerStory, reasonWord, statusWord, PROJECT_DETAIL_CAP, OPEN_STATUSES,
9
9
  } from './plans-model.js';
10
- import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, cantTell } from './ui.js';
10
+ import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, cantTell, firstPaint, arrivalScroll } from './ui.js';
11
11
  import { legacyHref } from './nav-model.js';
12
12
  import { topicNameMap } from './topic-names.js';
13
13
  import { topicLink } from './promises.js';
@@ -22,7 +22,7 @@ let last = null; // the last data read, repainted
22
22
 
23
23
  export async function renderPlans(main, ctx) {
24
24
  const { get, post } = ctx;
25
- if (!main.querySelector('.plans-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading plans' })));
25
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading plans' })));
26
26
  const [initiatives, digest, projects, blockers, floor, pool, topics] = await Promise.all([
27
27
  get('/initiatives'), get('/initiatives/digest'), get('/projects'), get('/blockers'), get('/autonomous/throughput-floor'), get('/pool'), get('/telegram/topics?scope=pool'),
28
28
  ]);
@@ -94,10 +94,7 @@ function paint() {
94
94
  let want = new URLSearchParams(path[1] || '').get('s');
95
95
  if (!want && /^work\/plans\/in-the-way/.test(path[0])) want = 'in-the-way';
96
96
  if (want) {
97
- const sec = main.querySelector(`#sec-${want}`);
98
- const head = sec && sec.querySelector('button.list-head[aria-expanded="false"]');
99
- if (head) head.click();
100
- if (sec) sec.scrollIntoView({ block: 'start' });
97
+ arrivalScroll(main.querySelector(`#sec-${want}`), { open: true });
101
98
  }
102
99
  }
103
100
 
@@ -4,7 +4,7 @@
4
4
  import { buildPromisesPage, storyOf, backstoryOf, updateQuestionOf, nextStepOf, actionsFor } from './promises-model.js';
5
5
  import { isInternalFollowUp } from './home-model.js';
6
6
  import { promiseActionLinks } from './promise-actions.js';
7
- import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, actionButton, note, cantTell } from './ui.js';
7
+ import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, actionButton, note, cantTell, firstPaint, arrivalScroll } from './ui.js';
8
8
  import { legacyHref } from './nav-model.js';
9
9
  import { verifiedDisplayName } from './display-identity.js';
10
10
 
@@ -19,7 +19,7 @@ export async function renderPromises(main, ctx) {
19
19
  if (main.dataset.promisesLoading === '1') return;
20
20
  main.dataset.promisesLoading = '1';
21
21
  const { get, post } = ctx;
22
- setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Work · Promises' }), el('h1', { class: 'page-title skeleton', text: 'Loading promises' })));
22
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'Work · Promises' }), el('h1', { class: 'page-title skeleton', text: 'Loading promises' })));
23
23
  const slow = setTimeout(() => {
24
24
  const title = main.querySelector('.page-title.skeleton');
25
25
  if (title) title.textContent = 'Still checking promises…';
@@ -67,10 +67,7 @@ export async function renderPromises(main, ctx) {
67
67
  // A fact links to its section: open it and scroll there.
68
68
  const want = new URLSearchParams((location.hash.split('?')[1] || '')).get('s');
69
69
  if (want) {
70
- const sec = main.querySelector(`#sec-${want}`);
71
- const head = sec && sec.querySelector('button.list-head[aria-expanded="false"]');
72
- if (head) head.click();
73
- if (sec) sec.scrollIntoView({ block: 'start' });
70
+ arrivalScroll(main.querySelector(`#sec-${want}`), { open: true });
74
71
  }
75
72
  // Home links to a specific request. Open its action sheet on arrival, so a
76
73
  // tap never strands the operator at the top of a long promise list.
@@ -10,7 +10,7 @@
10
10
  import {
11
11
  buildSchedulePage, buildHistory, markText, storySentence, storyLines, jobActions, toggleConfirm, runStartWords, runFollowWords, saveRefusalWords, originWords, scheduleWords, ago, HISTORY_LIMIT, STRIP_LIMIT,
12
12
  } from './schedule-model.js';
13
- import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, noteLinked, cantTell } from './ui.js';
13
+ import { el, setKids, pageHead, section, sheet, closeSheet, sheetIsOpen, story, details, note, noteLinked, cantTell, firstPaint, arrivalScroll } from './ui.js';
14
14
  import { legacyHref } from './nav-model.js';
15
15
  import { verifiedDisplayName } from './display-identity.js';
16
16
 
@@ -35,7 +35,7 @@ async function send(method, path, body) {
35
35
 
36
36
  export async function renderSchedule(main, ctx) {
37
37
  const { get } = ctx;
38
- if (!main.querySelector('.schedule-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading the schedule' })));
38
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: EYEBROW }), el('h1', { class: 'page-title skeleton', text: 'Loading the schedule' })));
39
39
  const [local, pool, machines, history, migration, reconcile] = await Promise.all([
40
40
  get('/jobs'), get('/jobs?scope=pool', 8000), get('/pool'), get('/jobs/history?limit=500'), get('/jobs/migration-status'), get('/jobs/reconcile'),
41
41
  ]);
@@ -82,10 +82,7 @@ function paint() {
82
82
  const params = new URLSearchParams(path[1] || '');
83
83
  const want = params.get('s');
84
84
  if (want) {
85
- const sec = main.querySelector(`#sec-${want}`);
86
- const head = sec && sec.querySelector('button.list-head[aria-expanded="false"]');
87
- if (head) head.click();
88
- if (sec) sec.scrollIntoView({ block: 'start' });
85
+ arrivalScroll(main.querySelector(`#sec-${want}`), { open: true });
89
86
  }
90
87
  const slug = params.get('job');
91
88
  if (slug && !sheetIsOpen()) {
@@ -9,7 +9,7 @@ import {
9
9
  topicNameMap, conversationChoices, requestCard, requestBody, testRequestBody, resultWords, shareWords,
10
10
  leftWords, TTL_CHOICES,
11
11
  } from './secrets-model.js';
12
- import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell } from './ui.js';
12
+ import { el, setKids, pageHead, section, sheet, closeSheet, story, details, note, cantTell, firstPaint } from './ui.js';
13
13
  import { legacyHref } from './nav-model.js';
14
14
 
15
15
  const RECORD_KEYS = ['token', 'topicId', 'createdAt', 'expiresAt'];
@@ -55,7 +55,7 @@ async function share(url, title, status) {
55
55
 
56
56
  export async function renderSecrets(main, ctx) {
57
57
  const { get } = ctx;
58
- if (!main.querySelector('.secrets-page')) setKids(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'People · Passwords & keys' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
58
+ firstPaint(main, el('section', { class: 'hero page-hero' }, el('div', { class: 'eyebrow', text: 'People · Passwords & keys' }), el('h1', { class: 'page-title skeleton', text: 'Loading' })));
59
59
  const [pendingRes, ctxRes, syncRes, topicsRes, poolRes] = await Promise.all([
60
60
  get('/secrets/pending'), get('/self-knowledge/session-context?full=1'), get('/secrets/sync-status'), get('/telegram/topics?scope=pool'), get('/pool'),
61
61
  ]);
@@ -205,7 +205,110 @@ export function actionButton({ label, kind = 'primary', confirm, reason, run, do
205
205
  }
206
206
 
207
207
  /** replaceChildren that skips null/false (a bare null would render as the text "null"). */
208
- export function setKids(node, ...kids) { node.replaceChildren(...kids.flat(Infinity).filter((k) => k != null && k !== false)); }
208
+ export function setKids(node, ...kids) {
209
+ const page = node.tagName === 'MAIN' && node.isConnected && node.childElementCount > 0 && !restoring ? capturePlace(node) : null;
210
+ if (node.tagName === 'MAIN') node.dataset.painted = '1';
211
+ node.replaceChildren(...kids.flat(Infinity).filter((k) => k != null && k !== false));
212
+ if (node.tagName !== 'MAIN' || restoring) return;
213
+ const drawn = foldStates(node);
214
+ if (page) restorePlace(node, page);
215
+ drawnFolds.set(node, drawn);
216
+ }
217
+
218
+ // ── A refresh keeps the operator's place (Dashboard UX Standard F12) ───────
219
+ // Every page repaints by replacing what is in <main>. Replacing and restoring in
220
+ // one task means the browser never paints the gap: no flash, no jump. Folds are
221
+ // matched by what they say (numbers ignored, a count going from 3 to 4 is the same
222
+ // fold), so this needs nothing from the page. Only a fold the operator moved away from
223
+ // how the page last drew it is put back; a page that keeps its own fold state (and
224
+ // repaints BECAUSE that state changed) is never fought.
225
+ let restoring = false;
226
+ const drawnFolds = new WeakMap(); // <main> → key → how the page itself last drew each fold
227
+ const foldIsOpen = (n) => (n.tagName === 'DETAILS' ? n.open : n.getAttribute('aria-expanded') === 'true');
228
+ function foldStates(root) {
229
+ const out = new Map();
230
+ for (const [n, key] of placeKeys(root)) if (n.tagName === 'DETAILS' || (n.tagName === 'BUTTON' && n.hasAttribute('aria-expanded'))) out.set(key, foldIsOpen(n));
231
+ return out;
232
+ }
233
+ function placeKeys(root) {
234
+ const seen = new Map();
235
+ const keys = new Map();
236
+ for (const n of root.querySelectorAll('*')) {
237
+ const tag = n.tagName;
238
+ const fold = tag === 'DETAILS' || (tag === 'BUTTON' && n.hasAttribute('aria-expanded'));
239
+ const scrolled = n.scrollTop > 0 || n.scrollLeft > 0;
240
+ const entry = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || tag === 'BUTTON' || tag === 'A' || n.hasAttribute('tabindex');
241
+ if (!fold && !scrolled && !entry && n.classList.length === 0) continue;
242
+ const home = n.parentElement && n.parentElement.closest('[id]');
243
+ const label = (tag === 'DETAILS' ? (n.querySelector(':scope > summary') || n).textContent : fold || entry ? (n.getAttribute('aria-label') || n.textContent || n.getAttribute('name') || n.getAttribute('placeholder') || '') : n.className) || '';
244
+ const base = `${tag}|${home && home !== root ? home.id : ''}|${String(label).replace(/\d+/g, '#').trim().slice(0, 80)}`;
245
+ const nth = seen.get(base) || 0;
246
+ seen.set(base, nth + 1);
247
+ keys.set(n, `${base}|${nth}`);
248
+ }
249
+ return keys;
250
+ }
251
+ function capturePlace(root) {
252
+ const place = { x: window.scrollX, y: window.scrollY, folds: new Map(), scrolls: new Map(), focus: null };
253
+ const drawn = drawnFolds.get(root) || new Map();
254
+ for (const [n, key] of placeKeys(root)) {
255
+ if ((n.tagName === 'DETAILS' || (n.tagName === 'BUTTON' && n.hasAttribute('aria-expanded'))) && drawn.has(key) && drawn.get(key) !== foldIsOpen(n)) place.folds.set(key, foldIsOpen(n));
256
+ if (n.scrollTop > 0 || n.scrollLeft > 0) place.scrolls.set(key, [n.scrollLeft, n.scrollTop]);
257
+ if (n === document.activeElement) place.focus = key;
258
+ }
259
+ return place;
260
+ }
261
+ function restorePlace(root, place) {
262
+ restoring = true;
263
+ try {
264
+ for (const [n, key] of placeKeys(root)) {
265
+ if (!place.folds.has(key) || !n.isConnected) continue;
266
+ const was = place.folds.get(key);
267
+ if (foldIsOpen(n) === was) continue;
268
+ if (n.tagName === 'DETAILS') n.open = was;
269
+ else n.click();
270
+ }
271
+ // Fold changes above can add height; positions are restored last, against the final layout.
272
+ for (const [n, key] of placeKeys(root)) {
273
+ const at = place.scrolls.get(key);
274
+ if (at) { n.scrollLeft = at[0]; n.scrollTop = at[1]; }
275
+ if (place.focus === key && typeof n.focus === 'function') n.focus({ preventScroll: true });
276
+ }
277
+ if (window.scrollX !== place.x || window.scrollY !== place.y) window.scrollTo(place.x, place.y);
278
+ } finally { restoring = false; }
279
+ }
280
+
281
+ /** The loading placeholder: only ever shown before a page's first real paint, never over real content. */
282
+ export function firstPaint(main, ...kids) {
283
+ if (main.dataset.painted) return;
284
+ main.replaceChildren(...kids.flat(Infinity).filter((k) => k != null && k !== false));
285
+ }
286
+
287
+ let arrived = new Set();
288
+ /** A new page or link arrived (console route()): its deep-link scroll may run again. */
289
+ export function newArrival() { arrived = new Set(); }
290
+ /** The one way to scroll to a deep-linked section: once per arrival, per key, never on a repaint. */
291
+ export function arrivalScroll(node, { key = 'page', open = false, block = 'start' } = {}) {
292
+ if (!node || arrived.has(key)) return false;
293
+ arrived.add(key);
294
+ if (open) { const head = node.querySelector('button.list-head[aria-expanded="false"]'); if (head) head.click(); }
295
+ if (typeof node.scrollIntoView === 'function') node.scrollIntoView({ block });
296
+ return true;
297
+ }
298
+
299
+ /** F9 at the shell: is the operator in the middle of something a repaint would destroy? */
300
+ export function refreshHeld(root) {
301
+ if (!root) return false;
302
+ if (root.querySelector('[data-interaction-open]')) return true;
303
+ const a = document.activeElement;
304
+ if (a && root.contains(a) && (a.tagName === 'TEXTAREA' || a.tagName === 'SELECT' || a.isContentEditable || (a.tagName === 'INPUT' && !/^(button|checkbox|radio|submit|reset|range|color|file)$/.test(a.type)))) return true;
305
+ for (const f of root.querySelectorAll('input, textarea')) {
306
+ if (/^(button|checkbox|radio|submit|reset|range|color|file|hidden)$/.test(f.type || '')) continue;
307
+ if (f.value !== f.defaultValue) return true;
308
+ }
309
+ const sel = typeof window.getSelection === 'function' ? window.getSelection() : null;
310
+ return Boolean(sel && !sel.isCollapsed && sel.anchorNode && root.contains(sel.anchorNode));
311
+ }
209
312
 
210
313
  // ── PIN sheet (00-patterns.md §4: one look, per-action adapters) ───────────
211
314
  // adapter 'session': POST /dashboard/unlock → operator-session token, held in page
@@ -10467,9 +10467,11 @@
10467
10467
  apiFetch('/shared-state/stats').catch(() => null),
10468
10468
  ]);
10469
10469
  const tbody = document.getElementById('ibEntriesTbody');
10470
+ // A read missed by a background refresh keeps the rows on screen (Dashboard UX F12).
10470
10471
  if (!recentRes || !recentRes.entries) {
10471
- tbody.innerHTML = '<tr><td colspan="7" style="padding:14px;color:var(--text-dim);text-align:center">Integrated-Being disabled or unavailable</td></tr>';
10472
+ if (!tbody.dataset.ibRows) tbody.innerHTML = '<tr><td colspan="7" style="padding:14px;color:var(--text-dim);text-align:center">Integrated-Being disabled or unavailable</td></tr>';
10472
10473
  } else {
10474
+ tbody.dataset.ibRows = '1';
10473
10475
  let entries = recentRes.entries;
10474
10476
  if (kind) entries = entries.filter(e => e.kind === kind);
10475
10477
  if (entries.length === 0) {
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "e9eb5bb7f56ce6f81c5f80210c1ec7999d4b8c7d2ff1ab5cfd97a269172ce2ac",
5
- "packageVersion": "1.3.1643",
5
+ "packageVersion": "1.3.1645",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "7b7c5c131b95619879efffeefa2814a65af619c1ae31a4efd1622e8e16245651",
2
+ "sha256": "0acd00d1676c2247df6020ca0a532c139bd5ad6020cc63f78ef676773d8c1e12",
3
3
  "registrySha256": "e9eb5bb7f56ce6f81c5f80210c1ec7999d4b8c7d2ff1ab5cfd97a269172ce2ac",
4
- "packageVersion": "1.3.1643"
4
+ "packageVersion": "1.3.1645"
5
5
  }