staysfixed 0.12.0 → 0.13.0

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/src/v2/doctor.js CHANGED
@@ -1757,7 +1757,7 @@ function describeSurfaces(tools, hosts, configured, browsers, desktopApp, driver
1757
1757
  name: 'command-line tools and libraries',
1758
1758
  status: wiredForCli ? 'ready' : 'partial',
1759
1759
  summary: wiredForCli
1760
- ? 'Fully covered here. What it printed, what it exited with, what it wrote, what it called out to, and what it exports.'
1760
+ ? 'What it printed, what it exited with, what it wrote, what it called out to, what it exports, and what each exported function answers when it is called with a fixed list of inputs. Not covered: any input outside that list, and any function whose name says it deletes, sends, publishes or charges — those are never called. A clean result covers what was walked; `staysfixed coverage` lists what was not.'
1761
1761
  : configured
1762
1762
  ? 'This machine can cover it in full — what a command printed, what it exited with, what it wrote, what it called out to, what it exports — but these settings wire no command to run and nothing to import, so a check runs none of it and a clean result says nothing about any of it.'
1763
1763
  : 'This machine can cover it in full, but nothing is set up in this folder yet, so a check cannot run here at all.',
@@ -2226,9 +2226,9 @@ function whatThisRunActuallyCovers(surfaces, setUpHere = true) {
2226
2226
  if (!setUpHere) {
2227
2227
  out.canRunHere = false;
2228
2228
  parts.push('Nothing is set up in this folder, so a check cannot run here at all and would cover nothing. Run `staysfixed init` first.');
2229
- parts.push(full.length > 0 ? `Once it is set up, this machine could cover ${plainList(out.covered)} in full.` : 'Even set up, this machine could cover nothing in full.');
2229
+ parts.push(full.length > 0 ? `Once it is set up, this machine can drive ${plainList(out.covered)}.` : 'Even set up, this machine can drive none of these.');
2230
2230
  } else {
2231
- parts.push(full.length > 0 ? `A check here covers ${plainList(out.covered)} in full.` : 'A check here covers nothing in full.');
2231
+ parts.push(full.length > 0 ? `A check here can drive ${plainList(out.covered)}. That is what this machine is able to walk, not what any run has walked.` : 'A check here can drive none of these.');
2232
2232
  }
2233
2233
  if (some.length > 0) parts.push(`It covers ${plainList(some.map((s) => s.name))} only partly — read the summary for each before treating a clean result as proof.`);
2234
2234
  if (missing.length > 0) {
@@ -437,7 +437,7 @@ export async function rememberCheck(store, what) {
437
437
  *
438
438
  * @typedef {object} Escalation
439
439
  * @property {string} id
440
- * @property {'sealed'|'budget'|'unpredictable'|'blocked'|'no-reference'} kind
440
+ * @property {'sealed'|'difference'|'budget'|'unpredictable'|'blocked'|'no-reference'} kind
441
441
  * @property {string} what What changed.
442
442
  * @property {string} why Why no agent could wave it through.
443
443
  * @property {string} todo What to do about it.
@@ -500,11 +500,26 @@ export async function escalationsFor(store, product) {
500
500
  * @returns {Escalations}
501
501
  */
502
502
  function buildEscalations(product, record, verdict) {
503
+ // TWO PILES, AND THE ORDER BETWEEN THEM IS THE FIX.
504
+ //
505
+ // `real` is everything that is the product behaving differently, or the check not having
506
+ // happened at all. `steadiness` is the one item that is neither: addresses that used to
507
+ // give the same answer every run and now do not. That is worth a person's attention and it
508
+ // is NOT a difference — nothing in it has a wrong value.
509
+ //
510
+ // WHAT WENT WRONG, 2026-08-31. A run caught one real change correctly and also measured 242
511
+ // addresses as newly unsteady. Only the second reached this block, because an ordinary
512
+ // difference has never been put in here at all — so the only sentence the owner read told
513
+ // him to hold the release over a wobble measurement, and never mentioned the change the
514
+ // tool had actually found. Piling them separately makes that ordering structural instead of
515
+ // accidental, and `crowdedOut` below makes sure a real change is never the thing left out.
503
516
  /** @type {Escalation[]} */
504
- const items = [];
517
+ const real = [];
518
+ /** @type {Escalation[]} */
519
+ const steadiness = [];
505
520
 
506
521
  if (verdict.blocked === true) {
507
- items.push({
522
+ real.push({
508
523
  id: 'blocked',
509
524
  kind: 'blocked',
510
525
  what: `Stays Fixed could not check ${product} at all on this run, so nothing about it has been proved either way.`,
@@ -512,7 +527,7 @@ function buildEscalations(product, record, verdict) {
512
527
  todo: `Something is in the way and it needs clearing — the run said: ${oneLine(verdict.summary, 200)}`,
513
528
  });
514
529
  } else if (!verdict.reference || verdict.reference.id === '') {
515
- items.push({
530
+ real.push({
516
531
  id: 'no-reference',
517
532
  kind: 'no-reference',
518
533
  what: `There is no build of ${product} on record as working yet, so this run had nothing to compare against.`,
@@ -527,7 +542,7 @@ function buildEscalations(product, record, verdict) {
527
542
 
528
543
  for (const f of record.findings) {
529
544
  if (f.unwaivable !== true) continue;
530
- items.push({
545
+ real.push({
531
546
  id: f.id,
532
547
  kind: 'sealed',
533
548
  what: oneLine(f.title, 220),
@@ -542,7 +557,7 @@ function buildEscalations(product, record, verdict) {
542
557
  }
543
558
 
544
559
  if (record.accounting.left === 0 && record.accounting.reported > record.accounting.unwaivable) {
545
- items.push({
560
+ real.push({
546
561
  id: 'budget',
547
562
  kind: 'budget',
548
563
  what: `The agent has used all ${record.accounting.budget} of the differences it is allowed to record as intended on ${product}, and there are still differences left over.`,
@@ -553,7 +568,7 @@ function buildEscalations(product, record, verdict) {
553
568
 
554
569
  if (record.newlyUnstable.length > 0) {
555
570
  const n = record.newlyUnstable.length;
556
- items.push({
571
+ steadiness.push({
557
572
  id: 'unpredictable',
558
573
  kind: 'unpredictable',
559
574
  what: `${n} ${n === 1 ? 'thing in' : 'things in'} ${product} used to give the same answer every single run and now ${n === 1 ? 'does' : 'do'} not: ${record.newlyUnstable.slice(0, 3).join(', ')}${n > 3 ? ', and more' : ''}.`,
@@ -561,15 +576,36 @@ function buildEscalations(product, record, verdict) {
561
576
  todo: 'Have it looked into before shipping. Something in the change made the product unpredictable.',
562
577
  paths: record.newlyUnstable.slice(0, 6),
563
578
  });
579
+ // A REAL CHANGE IS NEVER THE THING LEFT OUT.
580
+ //
581
+ // An ordinary difference is the agent's problem and does not get its own item — that is
582
+ // deliberate, and it stops a handful of items a month becoming a feed nobody reads. But it
583
+ // stops being right the moment the ONLY thing in this block is a wobble measurement,
584
+ // because then the sentence a person reads is an alarm about a non-difference with no
585
+ // mention of the difference the tool did find. So a real change gets one line here, and it
586
+ // goes first, exactly when it would otherwise have been the thing crowded out.
587
+ const crowdedOut = record.findings.filter((f) => f.waivedBy === undefined && f.unwaivable !== true);
588
+ if (real.length === 0 && crowdedOut.length > 0) {
589
+ const worst = crowdedOut[0];
590
+ real.push({
591
+ id: 'differences',
592
+ kind: 'difference',
593
+ what: `${product} behaves differently from the build you were happy with in ${crowdedOut.length} ${crowdedOut.length === 1 ? 'place' : 'places'}: ${oneLine(worst.title, 180)}${crowdedOut.length > 1 ? ', and more' : ''}.`,
594
+ why: 'This is a real change in the product, which is what the check is for — it is named before the steadiness note below so it cannot be read past.',
595
+ todo: 'The agent has to deal with each one: fix it, or record it as intended and say why. Nothing is the new normal until you ship.',
596
+ paths: (worst.paths ?? []).slice(0, 6),
597
+ });
598
+ }
564
599
  }
565
600
 
601
+ const all = [...real, ...steadiness];
566
602
  return {
567
603
  product,
568
604
  at: record.at,
569
- items,
605
+ items: all,
570
606
  waived: record.accounting.waived,
571
607
  expiredWaivers: record.accounting.expiredWaivers,
572
- note: summaryNote(product, items, record),
608
+ note: summaryNote(product, all, record),
573
609
  };
574
610
  }
575
611
 
@@ -595,11 +631,21 @@ function sealedTodo(f) {
595
631
  * @returns {string}
596
632
  */
597
633
  function summaryNote(product, items, record) {
634
+ // NOTHING NEEDING YOUR WORD IS NOT THE SAME AS NOTHING BEING WRONG.
635
+ //
636
+ // This line used to read "nothing on X needs your word" on a run that had found real
637
+ // differences the agent had not dealt with — true about who has to decide, and read by
638
+ // anybody skimming as an all-clear. One clause fixes it, and it is added rather than the
639
+ // sentence replaced, because who has to decide is still the thing this block is about.
640
+ const left = record.accounting.reported;
641
+ const outstanding = left > 0
642
+ ? ` The agent still has ${left} ${left === 1 ? 'difference' : 'differences'} of its own to deal with on this build.`
643
+ : '';
598
644
  if (items.length === 0) {
599
645
  const waived = record.accounting.waived;
600
646
  return waived > 0
601
- ? `Stays Fixed: nothing on ${product} needs your word. ${waived} ${waived === 1 ? 'difference was' : 'differences were'} recorded as intended by the agent and ${waived === 1 ? 'is' : 'are'} waiting on your next ship.`
602
- : `Stays Fixed: nothing on ${product} needs your word.`;
647
+ ? `Stays Fixed: nothing on ${product} needs your word.${outstanding} ${waived} ${waived === 1 ? 'difference was' : 'differences were'} recorded as intended by the agent and ${waived === 1 ? 'is' : 'are'} waiting on your next ship.`
648
+ : `Stays Fixed: nothing on ${product} needs your word.${outstanding}`;
603
649
  }
604
650
  return `Stays Fixed: ${items.length} ${items.length === 1 ? 'thing needs' : 'things need'} your word on ${product}.`;
605
651
  }