instar 1.3.44 → 1.3.46
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/dashboard/index.html +68 -14
- package/dashboard/process-health.js +204 -53
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +9 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +20 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts +16 -0
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +52 -6
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.45.md +28 -0
- package/upgrades/1.3.46.md +46 -0
- package/upgrades/side-effects/process-health-tab-eli16-pass.md +49 -0
package/dashboard/index.html
CHANGED
|
@@ -795,24 +795,74 @@
|
|
|
795
795
|
.ph-headline { font-size: 26px; font-weight: 600; line-height: 1.5; color: var(--text-bright); }
|
|
796
796
|
.ph-subline { font-size: 18px; color: var(--text-dim); margin-top: 8px; line-height: 1.55; }
|
|
797
797
|
.ph-section { display: flex; flex-direction: column; gap: 10px; }
|
|
798
|
-
.ph-h { font-size: 20px; font-weight: 600; color: var(--text-bright); line-height: 1.5; }
|
|
798
|
+
.ph-h { font-size: 20px; font-weight: 600; color: var(--text-bright); line-height: 1.5; margin: 0; }
|
|
799
|
+
.ph-h-sub { font-size: 15px; color: var(--text-dim); line-height: 1.55; margin: 4px 0 4px 0; max-width: 720px; }
|
|
799
800
|
.ph-empty { font-size: 18px; color: var(--text-dim); line-height: 1.6; }
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
801
|
+
/* Page intro — one-sentence grounding right under the title so terms like
|
|
802
|
+
"it" / "the system" later in the page have a clear referent. */
|
|
803
|
+
.ph-intro { font-size: 17px; color: var(--text); line-height: 1.55; margin: 0; max-width: 720px; }
|
|
804
|
+
/* Status color tokens — calm, never alarming. The dot is a scan-aid:
|
|
805
|
+
glance the collapsed list, see which problems are still being worked
|
|
806
|
+
on (amber), cause known (blue), or worked out (green). */
|
|
807
|
+
.ph-status-dot {
|
|
808
|
+
display: inline-block; width: 10px; height: 10px; border-radius: 50%;
|
|
809
|
+
flex-shrink: 0; margin-top: 8px; background: var(--text-dim);
|
|
810
|
+
}
|
|
811
|
+
.ph-status-dot.status-open { background: #f59e0b; } /* amber: still looking */
|
|
812
|
+
.ph-status-dot.status-attributed{ background: #60a5fa; } /* soft blue: cause known */
|
|
813
|
+
.ph-status-dot.status-verified { background: var(--ph-accent); }
|
|
814
|
+
.ph-status-dot.status-resolved { background: var(--ph-accent); }
|
|
815
|
+
.ph-status-dot.status-closed { background: #6b7280; } /* muted gray */
|
|
816
|
+
/* Labeled rows inside the expanded body — scaffolding, not paragraphs. */
|
|
817
|
+
.ph-label { font-weight: 600; color: var(--text); margin-right: 4px; }
|
|
818
|
+
.ph-value { color: var(--text-dim); }
|
|
819
|
+
/* Expandable items (captured problems + pattern cards): summary always
|
|
820
|
+
visible, body revealed on tap. Plain ELI16 inside both. */
|
|
821
|
+
.ph-item {
|
|
822
|
+
border: 1px solid var(--border); border-radius: 10px; padding: 0;
|
|
823
|
+
background: transparent;
|
|
803
824
|
}
|
|
825
|
+
.ph-item + .ph-item { margin-top: 10px; }
|
|
826
|
+
.ph-item-summary {
|
|
827
|
+
padding: 14px 44px 14px 16px; font-size: 18px; color: var(--text); line-height: 1.6;
|
|
828
|
+
cursor: pointer; list-style: none; position: relative;
|
|
829
|
+
display: flex; flex-direction: row; align-items: flex-start; gap: 12px;
|
|
830
|
+
}
|
|
831
|
+
.ph-item-text { flex: 1; display: flex; flex-direction: column; gap: 6px; }
|
|
832
|
+
.ph-item-summary::-webkit-details-marker { display: none; }
|
|
833
|
+
/* Subtle chevron on the right edge — signals "tap to expand" without shouting. */
|
|
834
|
+
.ph-item-summary::after {
|
|
835
|
+
content: '▾'; position: absolute; right: 16px; top: 16px;
|
|
836
|
+
color: var(--text-dim); font-size: 16px; opacity: 0.8;
|
|
837
|
+
}
|
|
838
|
+
.ph-item[open] > .ph-item-summary::after { content: '▴'; opacity: 1; }
|
|
839
|
+
.ph-item[open] > .ph-item-summary { color: var(--text-bright); border-bottom: 1px solid var(--border); }
|
|
840
|
+
.ph-item-body { padding: 14px 16px; display: flex; flex-direction: column; gap: 8px; }
|
|
841
|
+
.ph-item-row { font-size: 16px; color: var(--text-dim); line-height: 1.55; }
|
|
842
|
+
.ph-item-row.ph-card-rec { color: var(--text); }
|
|
843
|
+
.ph-item-inert { opacity: 0.7; }
|
|
844
|
+
/* Pattern-card-specific lines inside the summary stack. */
|
|
804
845
|
.ph-card-summary { font-size: 18px; color: var(--text-bright); line-height: 1.5; }
|
|
805
846
|
.ph-card-frame { font-size: 15px; color: var(--text-dim); font-style: italic; }
|
|
806
|
-
|
|
807
|
-
.ph-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
.ph-stage {
|
|
811
|
-
|
|
847
|
+
/* Maturation track: each stage is also an expandable row. */
|
|
848
|
+
.ph-stage {
|
|
849
|
+
padding: 4px 0; line-height: 1.5; color: var(--text-dim);
|
|
850
|
+
}
|
|
851
|
+
.ph-stage-summary {
|
|
852
|
+
display: flex; align-items: baseline; gap: 10px; font-size: 18px;
|
|
853
|
+
cursor: pointer; list-style: none; padding: 4px 0; position: relative;
|
|
854
|
+
}
|
|
855
|
+
.ph-stage-summary::-webkit-details-marker { display: none; }
|
|
856
|
+
.ph-stage-summary::after {
|
|
857
|
+
content: '▾'; margin-left: auto; color: var(--text-dim); font-size: 12px; opacity: 0.4;
|
|
858
|
+
}
|
|
859
|
+
.ph-stage[open] > .ph-stage-summary::after { content: '▴'; opacity: 0.8; }
|
|
860
|
+
.ph-stage-here > .ph-stage-summary { color: var(--text-bright); }
|
|
812
861
|
.ph-stage-glyph { color: var(--text-dim); }
|
|
813
862
|
.ph-stage-here .ph-stage-glyph { color: var(--ph-accent); }
|
|
814
863
|
.ph-stage-marker { color: var(--ph-accent); font-size: 15px; }
|
|
815
864
|
.ph-stage-note { color: var(--text-dim); font-size: 15px; }
|
|
865
|
+
.ph-stage-body { font-size: 16px; color: var(--text-dim); line-height: 1.55; padding: 6px 0 6px 26px; }
|
|
816
866
|
.ph-detail { margin-top: 4px; }
|
|
817
867
|
.ph-detail-summary { font-size: 17px; color: var(--text-dim); cursor: pointer; }
|
|
818
868
|
.ph-detail-body { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
|
|
@@ -3063,21 +3113,25 @@
|
|
|
3063
3113
|
<h2 class="ph-title">Process Health</h2>
|
|
3064
3114
|
<div id="phStamp" class="ph-stamp" aria-live="polite"></div>
|
|
3065
3115
|
</div>
|
|
3116
|
+
<p class="ph-intro">A quiet look at what the failure-watching system has spotted in this agent’s recent work — problems it’s caught, any patterns that keep coming up, and how far the system is switched on for this agent.</p>
|
|
3066
3117
|
<div id="phHeadline" class="ph-headline-wrap" aria-live="polite"></div>
|
|
3067
3118
|
<section class="ph-section">
|
|
3068
|
-
<h3 class="ph-h">
|
|
3119
|
+
<h3 class="ph-h">Recurring patterns</h3>
|
|
3120
|
+
<p class="ph-h-sub">Kinds of problem that have come up more than once across different changes.</p>
|
|
3069
3121
|
<div id="phPatterns"></div>
|
|
3070
3122
|
</section>
|
|
3071
3123
|
<section class="ph-section">
|
|
3072
|
-
<h3 class="ph-h">
|
|
3124
|
+
<h3 class="ph-h">Problems it’s noticed lately</h3>
|
|
3125
|
+
<p class="ph-h-sub">Individual things the system has caught. When the same kind keeps repeating, it shows up as a pattern above.</p>
|
|
3073
3126
|
<div id="phCaptured"></div>
|
|
3074
3127
|
</section>
|
|
3075
3128
|
<section class="ph-section">
|
|
3076
|
-
<h3 class="ph-h">
|
|
3129
|
+
<h3 class="ph-h">How far the system is switched on</h3>
|
|
3130
|
+
<p class="ph-h-sub">Where this agent sits on the rollout — from "off" to "on for everyone by default".</p>
|
|
3077
3131
|
<div id="phMaturation"></div>
|
|
3078
3132
|
</section>
|
|
3079
3133
|
<details class="ph-detail">
|
|
3080
|
-
<summary class="ph-detail-summary">
|
|
3134
|
+
<summary class="ph-detail-summary">More detail</summary>
|
|
3081
3135
|
<div id="phDetail" class="ph-detail-body"></div>
|
|
3082
3136
|
</details>
|
|
3083
3137
|
</div>
|
|
@@ -116,35 +116,51 @@ export function safeUrl(value) {
|
|
|
116
116
|
|
|
117
117
|
// ── Friendly wording (§4.2c) ───────────────────────────────────────────────
|
|
118
118
|
const CATEGORY_WORDS = {
|
|
119
|
-
concurrency: 'A
|
|
120
|
-
'config-parse': 'A
|
|
121
|
-
'type-error': 'A
|
|
122
|
-
'logic-error': 'A logic
|
|
123
|
-
'test-failure': 'A
|
|
124
|
-
'build-failure': '
|
|
125
|
-
'integration-gap':
|
|
126
|
-
regression: '
|
|
127
|
-
'wiring-gap': '
|
|
128
|
-
unknown: '
|
|
119
|
+
concurrency: 'A timing problem (two things ran at once)',
|
|
120
|
+
'config-parse': 'A settings problem',
|
|
121
|
+
'type-error': 'A data mix-up (wrong kind of value)',
|
|
122
|
+
'logic-error': 'A logic mistake',
|
|
123
|
+
'test-failure': 'A test that failed',
|
|
124
|
+
'build-failure': 'The build broke',
|
|
125
|
+
'integration-gap': "Two parts that didn't connect",
|
|
126
|
+
regression: 'Something that used to work broke',
|
|
127
|
+
'wiring-gap': "Something that wasn't hooked up",
|
|
128
|
+
unknown: 'A problem',
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
export function friendlyCategory(category) {
|
|
132
132
|
const key = typeof category === 'string' ? category : 'unknown';
|
|
133
|
-
return CATEGORY_WORDS[key] || '
|
|
133
|
+
return CATEGORY_WORDS[key] || 'A problem';
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// Short plural nouns for the Detail drawer's per-type counts (plain English, no
|
|
137
|
+
// raw category codes like "config-parse"). Unknown keys fall back to the
|
|
138
|
+
// sanitized raw key (defense-in-depth — these are server-controlled enum values).
|
|
139
|
+
const TYPE_WORDS = {
|
|
140
|
+
concurrency: 'Timing problems',
|
|
141
|
+
'config-parse': 'Settings problems',
|
|
142
|
+
'type-error': 'Data mix-ups',
|
|
143
|
+
'logic-error': 'Logic mistakes',
|
|
144
|
+
'test-failure': 'Failing tests',
|
|
145
|
+
'build-failure': 'Build breaks',
|
|
146
|
+
'integration-gap': 'Parts that didn’t connect',
|
|
147
|
+
regression: 'Things that broke again',
|
|
148
|
+
'wiring-gap': 'Things not hooked up',
|
|
149
|
+
unknown: 'Other',
|
|
150
|
+
};
|
|
151
|
+
|
|
136
152
|
// initiativeId → human label. NEVER expose the raw initiativeId (§4.2c). Seeded
|
|
137
153
|
// with the known instar initiatives; unmapped IDs fall back to a generic phrase.
|
|
138
154
|
export const ATTRIBUTION_LABELS = {
|
|
139
|
-
'failure-learning-loop': 'the failure-
|
|
140
|
-
'process-health-dashboard-tab': '
|
|
141
|
-
'ledger-spine': 'the
|
|
142
|
-
'threadline-keystone': 'the agent-
|
|
155
|
+
'failure-learning-loop': 'the failure-watching feature',
|
|
156
|
+
'process-health-dashboard-tab': 'this Process Health page',
|
|
157
|
+
'ledger-spine': 'the record-keeping feature',
|
|
158
|
+
'threadline-keystone': 'the agent-to-agent messaging feature',
|
|
143
159
|
};
|
|
144
160
|
|
|
145
161
|
export function attributionLabel(initiativeId, labelMap = ATTRIBUTION_LABELS) {
|
|
146
162
|
if (typeof initiativeId === 'string' && labelMap[initiativeId]) return labelMap[initiativeId];
|
|
147
|
-
return 'a
|
|
163
|
+
return 'a feature we were working on';
|
|
148
164
|
}
|
|
149
165
|
|
|
150
166
|
export function relativeTime(iso, now = Date.now()) {
|
|
@@ -188,16 +204,21 @@ export function renderHeadline(doc, target, { failures, stale, staleAgeMin }) {
|
|
|
188
204
|
target.replaceChildren();
|
|
189
205
|
if (stale) {
|
|
190
206
|
target.appendChild(
|
|
191
|
-
el(doc, 'div', 'ph-headline', `
|
|
207
|
+
el(doc, 'div', 'ph-headline', `Can't refresh right now — showing the last update${staleAgeMin != null ? ` from ${staleAgeMin} min ago` : ''}`),
|
|
192
208
|
);
|
|
193
|
-
target.appendChild(el(doc, 'div', 'ph-subline', "
|
|
209
|
+
target.appendChild(el(doc, 'div', 'ph-subline', "We'll keep trying — nothing is lost."));
|
|
194
210
|
return;
|
|
195
211
|
}
|
|
196
212
|
const list = Array.isArray(failures) ? failures : [];
|
|
197
213
|
const n = list.length;
|
|
198
214
|
const open = list.filter((f) => f && f.status !== 'resolved' && f.status !== 'closed').length;
|
|
199
|
-
|
|
200
|
-
|
|
215
|
+
const headline = n === 0 ? 'Keeping an eye out — all quiet so far' : `Keeping an eye out — ${n} thing${n === 1 ? '' : 's'} noticed so far`;
|
|
216
|
+
target.appendChild(el(doc, 'div', 'ph-headline', headline));
|
|
217
|
+
const subline = n === 0
|
|
218
|
+
? "Nothing has come up yet. We're just quietly watching — no alerts."
|
|
219
|
+
: open === 0
|
|
220
|
+
? "We know what caused each one. Just quietly watching — no alerts yet."
|
|
221
|
+
: `Still looking into ${open} of them. Just quietly watching — no alerts yet.`;
|
|
201
222
|
target.appendChild(el(doc, 'div', 'ph-subline', subline));
|
|
202
223
|
}
|
|
203
224
|
|
|
@@ -214,9 +235,9 @@ export function renderDisabled(doc, els) {
|
|
|
214
235
|
det.setAttribute('class', 'ph-operator');
|
|
215
236
|
const sum = doc.createElement('summary');
|
|
216
237
|
sum.setAttribute('class', 'ph-operator-summary');
|
|
217
|
-
sum.textContent = '
|
|
238
|
+
sum.textContent = 'For whoever set this up';
|
|
218
239
|
det.appendChild(sum);
|
|
219
|
-
det.appendChild(el(doc, 'p', 'ph-operator-hint', '
|
|
240
|
+
det.appendChild(el(doc, 'p', 'ph-operator-hint', 'You can switch it on in this agent’s settings.'));
|
|
220
241
|
els.headline.appendChild(det);
|
|
221
242
|
}
|
|
222
243
|
for (const k of ['patterns', 'captured', 'maturation', 'detail']) {
|
|
@@ -225,38 +246,139 @@ export function renderDisabled(doc, els) {
|
|
|
225
246
|
if (els.stamp) els.stamp.textContent = '';
|
|
226
247
|
}
|
|
227
248
|
|
|
228
|
-
/**
|
|
249
|
+
/** Insight lifecycle wording — informational only, no action-implying language. */
|
|
250
|
+
const INSIGHT_STATUS_WORDS = {
|
|
251
|
+
discovered: 'Just noticed',
|
|
252
|
+
'acted-on': 'Being looked into',
|
|
253
|
+
verified: 'Confirmed no longer happening',
|
|
254
|
+
reopened: 'Came back after the previous attempt',
|
|
255
|
+
};
|
|
256
|
+
function friendlyInsightStatus(status) {
|
|
257
|
+
return INSIGHT_STATUS_WORDS[status] || 'Not set';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Patterns cards (§4.2b — awareness-only, NO action authority). Each card is
|
|
262
|
+
* an expandable <details> element: the headline summary is visible by default;
|
|
263
|
+
* the recommendation, evidence breakdown, and lifecycle are revealed on tap.
|
|
264
|
+
* The renderer-owned "verify before acting" framing stays on the summary so
|
|
265
|
+
* the no-action-authority signal is never hidden behind a tap.
|
|
266
|
+
*/
|
|
229
267
|
export function renderPatterns(doc, target, insights) {
|
|
230
268
|
if (!target) return;
|
|
231
269
|
target.replaceChildren();
|
|
232
270
|
const list = Array.isArray(insights) ? insights : [];
|
|
233
271
|
if (list.length === 0) {
|
|
234
272
|
target.appendChild(
|
|
235
|
-
el(doc, 'p', 'ph-empty', 'Nothing
|
|
273
|
+
el(doc, 'p', 'ph-empty', 'Nothing to flag yet. We need to see a few more different problems before a real pattern shows up — totally normal this early.'),
|
|
236
274
|
);
|
|
237
275
|
return;
|
|
238
276
|
}
|
|
239
277
|
for (const ins of list) {
|
|
240
|
-
const card =
|
|
241
|
-
card.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
278
|
+
const card = doc.createElement('details');
|
|
279
|
+
card.setAttribute('class', 'ph-item ph-card');
|
|
280
|
+
const sum = doc.createElement('summary');
|
|
281
|
+
sum.setAttribute('class', 'ph-item-summary');
|
|
282
|
+
sum.appendChild(statusDot(doc, ins && ins.status));
|
|
283
|
+
// Just the pattern headline — the section title + subtitle above already
|
|
284
|
+
// establish "this is a kind of problem that has repeated", so a per-card
|
|
285
|
+
// restatement would be the third echo of the same idea. The "informational
|
|
286
|
+
// only, not a prompt to act" signal is structural now (the rendered card
|
|
287
|
+
// has nothing actionable on it), not chrome.
|
|
288
|
+
const text = el(doc, 'div', 'ph-item-text');
|
|
289
|
+
text.appendChild(el(doc, 'div', 'ph-card-summary', sanitizeForDisplay(ins && ins.summary, 'summary')));
|
|
290
|
+
sum.appendChild(text);
|
|
291
|
+
card.appendChild(sum);
|
|
292
|
+
// Body: same fixed label vocabulary as captured items, in the same order.
|
|
293
|
+
// Status · Times seen · First noticed. (Where/Cause don't apply to a class.)
|
|
294
|
+
// The recommendation field is intentionally omitted: this tab informs,
|
|
295
|
+
// doesn't direct — actionable insight is the deferred §10 surface.
|
|
296
|
+
const body = el(doc, 'div', 'ph-item-body');
|
|
297
|
+
body.appendChild(labeledRow(doc, 'Status', friendlyInsightStatus(ins && ins.status)));
|
|
247
298
|
const ds = ins && Number.isFinite(ins.distinctSessions) ? ins.distinctSessions : 0;
|
|
248
|
-
|
|
299
|
+
const dc = ins && Number.isFinite(ins.distinctCauseCommits) ? ins.distinctCauseCommits : 0;
|
|
300
|
+
const timesSeen = dc > 0
|
|
301
|
+
? `Across ${ds} different change${ds === 1 ? '' : 's'} (from ${dc} separate fix${dc === 1 ? '' : 'es'})`
|
|
302
|
+
: `Across ${ds} different change${ds === 1 ? '' : 's'}`;
|
|
303
|
+
body.appendChild(labeledRow(doc, 'Times seen', timesSeen));
|
|
304
|
+
if (ins && ins.discoveredAt) {
|
|
305
|
+
body.appendChild(labeledRow(doc, 'First noticed', relativeTime(ins.discoveredAt)));
|
|
306
|
+
}
|
|
307
|
+
card.appendChild(body);
|
|
249
308
|
target.appendChild(card);
|
|
250
309
|
}
|
|
251
310
|
}
|
|
252
311
|
|
|
253
|
-
/**
|
|
312
|
+
/**
|
|
313
|
+
* Friendly status / cause-confidence wording for the expandable body (§4.2c).
|
|
314
|
+
* Plain ELI16, no Instar-internal jargon. Unknown values fall back to a generic
|
|
315
|
+
* phrase — the body never shows raw status/attribution codes.
|
|
316
|
+
*/
|
|
317
|
+
// Terser status values — these pair with a bold "Status:" label in the body
|
|
318
|
+
// (so the value carries the state, the label carries the noun).
|
|
319
|
+
const STATUS_WORDS = {
|
|
320
|
+
open: 'Still looking into this',
|
|
321
|
+
attributed: 'We know what caused it',
|
|
322
|
+
verified: 'Confirmed worked out',
|
|
323
|
+
resolved: 'Worked out',
|
|
324
|
+
closed: 'Closed',
|
|
325
|
+
};
|
|
326
|
+
function friendlyStatus(status) {
|
|
327
|
+
return STATUS_WORDS[status] || 'Not set';
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const CAUSE_CONFIDENCE_WORDS = {
|
|
331
|
+
automatic: 'Auto-spotted from the fix commit — we’re confident',
|
|
332
|
+
'one-tap': 'Tagged by hand by whoever found it',
|
|
333
|
+
inferred: 'Best guess from the surrounding context',
|
|
334
|
+
};
|
|
335
|
+
function friendlyCauseConfidence(attribution) {
|
|
336
|
+
return CAUSE_CONFIDENCE_WORDS[attribution] || 'Not recorded';
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Map an upstream status (failure or insight) to a status-dot CSS class.
|
|
341
|
+
* The dot is a scan-aid only — colors are calm (amber/blue/green/gray),
|
|
342
|
+
* never alarm reds.
|
|
343
|
+
*/
|
|
344
|
+
export function statusDotClass(status) {
|
|
345
|
+
switch (status) {
|
|
346
|
+
case 'open': case 'reopened': case 'discovered': return 'status-open';
|
|
347
|
+
case 'attributed': case 'acted-on': return 'status-attributed';
|
|
348
|
+
case 'verified': case 'resolved': return 'status-verified';
|
|
349
|
+
case 'closed': return 'status-closed';
|
|
350
|
+
default: return 'status-closed';
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Build a labeled-row inside the expanded body: `<bold-label>:<value>`. Keeps
|
|
356
|
+
* the structure scannable without paragraph-walls. Both halves are textContent
|
|
357
|
+
* (the safety contract is unaffected).
|
|
358
|
+
*/
|
|
359
|
+
function labeledRow(doc, label, value) {
|
|
360
|
+
const row = el(doc, 'div', 'ph-item-row');
|
|
361
|
+
row.appendChild(el(doc, 'span', 'ph-label', `${label}:`));
|
|
362
|
+
row.appendChild(doc.createTextNode(' '));
|
|
363
|
+
row.appendChild(el(doc, 'span', 'ph-value', value));
|
|
364
|
+
return row;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Status-dot span (always at the left edge of an item summary; color-coded by status). */
|
|
368
|
+
function statusDot(doc, status) {
|
|
369
|
+
const dot = doc.createElement('span');
|
|
370
|
+
dot.setAttribute('class', `ph-status-dot ${statusDotClass(status)}`);
|
|
371
|
+
dot.setAttribute('aria-hidden', 'true');
|
|
372
|
+
return dot;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** What's been captured (§4.2c — operator-safe subset only, expandable for depth on demand). */
|
|
254
376
|
export function renderCaptured(doc, target, failures, labelMap = ATTRIBUTION_LABELS, now = Date.now()) {
|
|
255
377
|
if (!target) return;
|
|
256
378
|
target.replaceChildren();
|
|
257
379
|
const list = Array.isArray(failures) ? failures.slice(0, 10) : [];
|
|
258
380
|
if (list.length === 0) {
|
|
259
|
-
target.appendChild(el(doc, 'p', 'ph-empty', '
|
|
381
|
+
target.appendChild(el(doc, 'p', 'ph-empty', 'Nothing yet — that just means nothing has come up since this was switched on.'));
|
|
260
382
|
return;
|
|
261
383
|
}
|
|
262
384
|
for (const f of list) {
|
|
@@ -265,22 +387,45 @@ export function renderCaptured(doc, target, failures, labelMap = ATTRIBUTION_LAB
|
|
|
265
387
|
const summary = sanitizeForDisplay(f.summary, 'summary');
|
|
266
388
|
const label = attributionLabel(f.initiativeId, labelMap); // NEVER raw initiativeId
|
|
267
389
|
const when = relativeTime(f.detectedAt, now);
|
|
268
|
-
//
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
390
|
+
// The collapsed summary line — one plain-English sentence, as before.
|
|
391
|
+
const summaryText = `${cat}: ${summary}${CHROME.sep}came up while building ${label}${CHROME.sep}${when}.`;
|
|
392
|
+
const item = doc.createElement('details');
|
|
393
|
+
item.setAttribute('class', isMixedScript(f.summary) ? 'ph-item ph-item-inert' : 'ph-item');
|
|
394
|
+
const sum = doc.createElement('summary');
|
|
395
|
+
sum.setAttribute('class', 'ph-item-summary');
|
|
396
|
+
sum.appendChild(statusDot(doc, f.status));
|
|
397
|
+
sum.appendChild(el(doc, 'div', 'ph-item-text', summaryText));
|
|
398
|
+
item.appendChild(sum);
|
|
399
|
+
// Expanded body: a fixed-shape fact sheet. Same labels in the same order
|
|
400
|
+
// are used by patterns (§4.2b) where applicable — predictable scanning.
|
|
401
|
+
// Order: Status · Where · Times seen · First noticed · Cause.
|
|
402
|
+
const body = el(doc, 'div', 'ph-item-body');
|
|
403
|
+
body.appendChild(labeledRow(doc, 'Status', friendlyStatus(f.status)));
|
|
404
|
+
if (f.detail && typeof f.detail.redacted === 'string' && f.detail.redacted.trim()) {
|
|
405
|
+
body.appendChild(labeledRow(doc, 'Where', sanitizeForDisplay(f.detail.redacted, 'detail')));
|
|
406
|
+
}
|
|
407
|
+
const occ = Number.isFinite(f.occurrenceCount) ? f.occurrenceCount : 1;
|
|
408
|
+
body.appendChild(labeledRow(doc, 'Times seen', occ > 1 ? `${occ} times so far` : 'Just once'));
|
|
409
|
+
body.appendChild(labeledRow(doc, 'First noticed', relativeTime(f.detectedAt, now)));
|
|
410
|
+
body.appendChild(labeledRow(doc, 'Cause', friendlyCauseConfidence(f.attribution)));
|
|
411
|
+
item.appendChild(body);
|
|
412
|
+
target.appendChild(item);
|
|
273
413
|
}
|
|
274
414
|
}
|
|
275
415
|
|
|
276
416
|
/** Maturation track (§4.2d). 4th stage is permanently-future (no per-agent flag). */
|
|
277
417
|
const STAGES = [
|
|
278
|
-
{ key: 'dark', label: '
|
|
279
|
-
{ key: 'capture-only', label: '
|
|
280
|
-
{ key: 'insight-push', label: '
|
|
281
|
-
{ key: 'default-on', label: '
|
|
418
|
+
{ key: 'dark', label: 'Not switched on yet', description: 'Not switched on for this agent yet — nothing is being recorded.' },
|
|
419
|
+
{ key: 'capture-only', label: 'Quietly watching', description: 'Quietly recording what comes up. No alerts go anywhere — we’re just building up evidence to learn from.' },
|
|
420
|
+
{ key: 'insight-push', label: 'Watching + flagging patterns to you', description: 'Same quiet recording, plus a heads-up when the same kind of problem keeps showing up. Only fires on real patterns, never on one-offs.' },
|
|
421
|
+
{ key: 'default-on', label: 'On for everyone by default', description: 'Switched on for every agent by default — the fully-rolled-out end state.' },
|
|
282
422
|
];
|
|
283
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Maturation track (§4.2d). Each stage is an expandable <details> row so the
|
|
426
|
+
* label stays glanceable but the meaning is one tap away. The 4th stage is
|
|
427
|
+
* permanently-future (no per-agent flag).
|
|
428
|
+
*/
|
|
284
429
|
export function renderMaturation(doc, target, rollout) {
|
|
285
430
|
if (!target) return;
|
|
286
431
|
target.replaceChildren();
|
|
@@ -290,12 +435,17 @@ export function renderMaturation(doc, target, rollout) {
|
|
|
290
435
|
const s = STAGES[i];
|
|
291
436
|
const isHere = i === currentIdx;
|
|
292
437
|
const done = currentIdx >= 0 && i < currentIdx;
|
|
293
|
-
const row =
|
|
438
|
+
const row = doc.createElement('details');
|
|
439
|
+
row.setAttribute('class', isHere ? 'ph-stage ph-stage-here' : 'ph-stage');
|
|
440
|
+
const sum = doc.createElement('summary');
|
|
441
|
+
sum.setAttribute('class', 'ph-stage-summary');
|
|
294
442
|
// glyph + label + marker are ALL renderer-owned static literals
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (isHere)
|
|
298
|
-
else if (done)
|
|
443
|
+
sum.appendChild(el(doc, 'span', 'ph-stage-glyph', done || isHere ? CHROME.stageDone : CHROME.stageFuture));
|
|
444
|
+
sum.appendChild(el(doc, 'span', 'ph-stage-label', s.label));
|
|
445
|
+
if (isHere) sum.appendChild(el(doc, 'span', 'ph-stage-marker', CHROME.hereMarker));
|
|
446
|
+
else if (done) sum.appendChild(el(doc, 'span', 'ph-stage-note', 'done'));
|
|
447
|
+
row.appendChild(sum);
|
|
448
|
+
row.appendChild(el(doc, 'div', 'ph-stage-body', s.description));
|
|
299
449
|
target.appendChild(row);
|
|
300
450
|
}
|
|
301
451
|
}
|
|
@@ -309,16 +459,17 @@ export function renderDetail(doc, target, analysis) {
|
|
|
309
459
|
return;
|
|
310
460
|
}
|
|
311
461
|
const add = (label, val) => target.appendChild(el(doc, 'div', 'ph-detail-row', `${label}: ${val}`));
|
|
312
|
-
add('Total
|
|
313
|
-
add('
|
|
314
|
-
add('Not yet
|
|
462
|
+
add('Total noticed', String(analysis.total ?? 0));
|
|
463
|
+
add('Ones we know the cause of', String(analysis.attributed ?? 0));
|
|
464
|
+
add('Not yet tied to a feature', String(analysis.noFeatureLink ?? 0));
|
|
315
465
|
const byCat = analysis.byCategory && typeof analysis.byCategory === 'object' ? analysis.byCategory : {};
|
|
316
466
|
for (const [k, v] of Object.entries(byCat)) {
|
|
317
|
-
|
|
467
|
+
const typeLabel = TYPE_WORDS[k] || sanitizeForDisplay(k, 'label');
|
|
468
|
+
add(typeLabel, String(v));
|
|
318
469
|
}
|
|
319
470
|
const unknown = analysis.unknownToolchainByAuthor && typeof analysis.unknownToolchainByAuthor === 'object' ? analysis.unknownToolchainByAuthor : {};
|
|
320
471
|
const unknownTotal = Object.values(unknown).reduce((a, b) => a + (Number(b) || 0), 0);
|
|
321
|
-
add('
|
|
472
|
+
add("Ones where we're not sure which tools were involved", String(unknownTotal));
|
|
322
473
|
}
|
|
323
474
|
|
|
324
475
|
// ── Polling controller (§4.3 — visibility-gated, abort-safe, coordinated, diff-aware) ──
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+RH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAksDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+RH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAksDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAksNtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
|
package/dist/commands/server.js
CHANGED
|
@@ -4792,7 +4792,15 @@ export async function startServer(options) {
|
|
|
4792
4792
|
// Phase 1C of GRACEFUL_UPDATES: reduce noise for routine maintenance.
|
|
4793
4793
|
const runningSessions = sessionManager.listRunningSessions();
|
|
4794
4794
|
if (runningSessions.length > 0) {
|
|
4795
|
-
|
|
4795
|
+
// Route the pre-restart heads-up to the dedicated Agent Updates
|
|
4796
|
+
// topic, not the central-notify default (Attention). Every other
|
|
4797
|
+
// update-class emitter (AutoUpdater, AutoDispatcher, the restart
|
|
4798
|
+
// handshake, /telegram/post-update) routes here; this caller was
|
|
4799
|
+
// the lone path going to Attention. Falls through to the central
|
|
4800
|
+
// default when Updates is unset, preserving prior behavior for
|
|
4801
|
+
// agents without an Updates topic.
|
|
4802
|
+
const updatesTopicId = state.get('agent-updates-topic') || undefined;
|
|
4803
|
+
notify('IMMEDIATE', 'system', `Applying update to v${request.targetVersion} — restarting now. Active sessions will resume automatically.`, updatesTopicId);
|
|
4796
4804
|
}
|
|
4797
4805
|
else {
|
|
4798
4806
|
console.log(`[ForegroundRestartWatcher] Silent restart — no active sessions (v${request.previousVersion} → v${request.targetVersion})`);
|