[object Object]

← back to Gated Morning Review

viewer v3: add 'What I'd do' suggested-action column (12yr plain language, old/stale + task-status aware)

4708dcbccf7d0626911594d096668596fa927ad6 · 2026-08-20 09:18:46 -0700 · steve

Files touched

Diff

commit 4708dcbccf7d0626911594d096668596fa927ad6
Author: steve <steve@designerwallcoverings.com>
Date:   Thu Aug 20 09:18:46 2026 -0700

    viewer v3: add 'What I'd do' suggested-action column (12yr plain language, old/stale + task-status aware)
---
 lib.mjs    | 34 ++++++++++++++++++++++++----------
 server.mjs |  3 +++
 2 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/lib.mjs b/lib.mjs
index 4cd044c..4580f04 100644
--- a/lib.mjs
+++ b/lib.mjs
@@ -27,31 +27,38 @@ const CAT = {
   golive: { big: '🌐 Put something LIVE on the internet',
     why: "Right now this thing is NOT online where people can see it. This puts it live on the web.",
     good: "More people can find and use it — could bring visitors or sales.",
-    bad: "Going live is hard to undo (it changes DNS + servers). If it's not ready, or it's already live, skip it." },
+    bad: "Going live is hard to undo (it changes DNS + servers). If it's not ready, or it's already live, skip it.",
+    sug: "🙋 YOU do this one — it goes LIVE to everyone. Make sure it's really ready first." },
   money: { big: '💳 Money / payments',
     why: "This is about REAL money — turning on charging, or fixing a price that's wrong.",
     good: "Stops us losing money or lets us start earning.",
-    bad: "Real money = real risk. If the numbers are old, it could charge or price the wrong amount." },
+    bad: "Real money = real risk. If the numbers are old, it could charge or price the wrong amount.",
+    sug: "🙋 Needs YOU — it's real money. Only run it if the numbers are fresh." },
   store: { big: '🏷️ Change the online store (shoppers see it)',
     why: "This changes what shoppers see in the store — a product, a price, a picture, or a link.",
     good: "Makes the store correct so shoppers see the right thing (and Google is happy).",
-    bad: "It's LIVE to customers. If the info inside is old, it could change the wrong products." },
+    bad: "It's LIVE to customers. If the info inside is old, it could change the wrong products.",
+    sug: "✅ Usually safe to run — it's a small store fix you can undo." },
   system: { big: '🔧 System / security setup',
     why: "This sets up something behind the scenes — a server, a timer/schedule, or a password.",
     good: "Keeps the machines healthy, safe, and running on their own.",
-    bad: "Touches servers or passwords. Done at the wrong time it can break things or make a duplicate job." },
+    bad: "Touches servers or passwords. Done at the wrong time it can break things or make a duplicate job.",
+    sug: "🙋 Your hands — it touches servers or passwords. Safe, but needs you." },
   social: { big: '📣 Social-media posting',
     why: "This posts something to a PUBLIC social account (like Instagram or TikTok).",
     good: "Gets our stuff seen by lots more people for free.",
-    bad: "It's public and hard to unsend. A wrong or repeated post looks bad." },
+    bad: "It's public and hard to unsend. A wrong or repeated post looks bad.",
+    sug: "📣 Only if you're sure — a public post is hard to un-post." },
   send: { big: '✉️ Send a message to someone',
     why: "This sends an email or message OUT to a person (a customer or a vendor).",
     good: "Reaches someone we actually need to talk to.",
-    bad: "Once it's sent you can't unsend it — make sure it's right and wanted first." },
+    bad: "Once it's sent you can't unsend it — make sure it's right and wanted first.",
+    sug: "✉️ Only if it's right — you can't unsend an email." },
   other: { big: '📋 A change that needs your OK',
     why: "This is a change someone set up that's waiting for your yes-or-no.",
     good: "Ticks a to-do off the list.",
-    bad: "If it's old or already handled, it's just clutter — safe to toss." },
+    bad: "If it's old or already handled, it's just clutter — safe to toss.",
+    sug: "🤔 Just pick yes or no — it's a choice, not a chore." },
 };
 
 export function classify(title, head, file) {
@@ -66,7 +73,7 @@ export function classify(title, head, file) {
   else if (has(/instagram|\big\b|tiktok|youtube|social|post batch|roster|@\w/)) key = 'social';
   else if (has(/\bemail\b|\bsend\b|blast|letter|nudge|mailer/)) key = 'send';
   const c = CAT[key];
-  return { category: key, big: c.big, why: c.why, good: c.good, bad: c.bad, staleSig };
+  return { category: key, big: c.big, why: c.why, good: c.good, bad: c.bad, sug: c.sug, staleSig };
 }
 
 export function scanQueue() {
@@ -84,9 +91,13 @@ export function scanQueue() {
     const aging = age >= AGING_DAYS;
     // aging items get an extra "stale-data" warning appended to the bad reason
     const bad = aging ? c.bad + " ⚠️ And it's OLD — the info inside may be out of date, so re-check before running." : c.bad;
+    // my suggestion, in plain kid words — with an old/stale overlay
+    const suggest = c.staleSig ? "🗑️ Probably already done — safe to toss."
+      : aging ? "⚠️ It's OLD — first make sure the info is still true, THEN " + c.sug.replace(/^..? /, "").toLowerCase()
+      : c.sug;
     return { kind: 'gated', id: f, file: f, title, ticket, age,
       ageWord: age <= 0 ? 'today' : age === 1 ? '1 day old' : `${age} days old`,
-      ...c, bad, aging, disposition: (c.staleSig || age > STALE_DAYS) ? 'STALE' : 'DECIDE' };
+      ...c, bad, suggest, aging, disposition: (c.staleSig || age > STALE_DAYS) ? 'STALE' : 'DECIDE' };
   }).filter(Boolean).sort((a, b) => b.age - a.age);
 }
 
@@ -101,8 +112,11 @@ export function scanTasks(limit = 60) {
     if (!m) continue;
     const [, id, status, owner, project, title] = m;
     const c = classify(title, '', id);
+    const suggest = status === 'blocked' ? "⏳ Stuck waiting on something else — can't run it yet."
+      : status === 'doing' ? "⚙️ A robot's already working on it — leave it alone."
+      : c.sug;
     items.push({ kind: 'task', id, status, owner, project, title: title.slice(0, 140),
-      ...c, blocked: status === 'blocked', doing: status === 'doing' });
+      ...c, suggest, blocked: status === 'blocked', doing: status === 'doing' });
     if (items.length >= limit) break;
   }
   return items;
diff --git a/server.mjs b/server.mjs
index 78343b8..e7e465d 100644
--- a/server.mjs
+++ b/server.mjs
@@ -66,6 +66,7 @@ const card = (it) => {
         <div class=raw>${(it.title||'').replace(/</g,'&lt;')}</div></div>
       <div class=col><div class=lbl>👍 Good reason to keep &amp; run</div><div class="txt gd">${it.good}</div>
         <div class=lbl style=margin-top:10px>👎 Reason it's OK to close</div><div class="txt bd">${it.bad}</div></div>
+      <div class="col sugcol"><div class=lbl>🤔 What I'd do</div><div class="txt sg">${it.suggest||''}</div></div>
     </div>
     <div class=btns>
       <button class=run onclick="run('${runId}','${it.kind}',this)">▶️ Run in iTerm2</button>
@@ -91,6 +92,8 @@ h1{font-size:38px;margin:6px 0}.sub{font-size:20px;color:#6b6257;margin-bottom:1
 .cols{display:flex;gap:18px;flex-wrap:wrap}.col{flex:1;min-width:260px}
 .lbl{font-size:15px;font-weight:800;color:#6b6257;text-transform:uppercase;letter-spacing:.3px}
 .txt{font-size:18px;margin:3px 0 0}.gd{color:#1f6b3a}.bd{color:#8a4b12}
+.sugcol{background:#fff8e6;border:2px solid #f0d98a;border-radius:12px;padding:12px 14px;min-width:230px}
+.sg{font-size:19px;font-weight:700;color:#5b4a12}
 .raw{font-size:13px;color:#9a8f80;margin-top:8px;word-break:break-word}
 .btns{display:flex;gap:12px;margin-top:16px}
 button{font-size:20px;font-weight:700;padding:14px 20px;border:0;border-radius:13px;cursor:pointer;flex:1}

← bc59465 viewer: honest Run-button note — queues to _approved + loop  ·  back to Gated Morning Review  ·  morning-review: dedupe queue — hide tasks that duplicate a g e3a8b5e →