[object Object]

← back to Approval Command Center

ACC: dynamic file scan for new pending-approval items (was stale manifest-only)

4af2af79c4e76231de57a56bf386a4e08b9badf2 · 2026-08-11 04:09:58 -0700 · Steve Abrams

Files touched

Diff

commit 4af2af79c4e76231de57a56bf386a4e08b9badf2
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 11 04:09:58 2026 -0700

    ACC: dynamic file scan for new pending-approval items (was stale manifest-only)
---
 public/index.html |  2 +-
 server.js         | 42 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/public/index.html b/public/index.html
index f1cec81..9dc02b7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,7 +3,7 @@
 <head>
 <meta charset="utf-8"/>
 <meta name="viewport" content="width=device-width, initial-scale=1"/>
-<title>Approval Command Center — 109 gated drafts</title>
+<title>Approval Command Center</title>
 <style>
   :root{ --cols:3; --bg:#0f1115; --panel:#171a21; --line:#252a34; --ink:#e7ebf0; --dim:#98a2b3; --teal:#33c2b8; --gold:#d9b166; }
   *{box-sizing:border-box}
diff --git a/server.js b/server.js
index 275b15f..ef8d73b 100644
--- a/server.js
+++ b/server.js
@@ -54,9 +54,49 @@ function severity(gate, reversible) {
 }
 
 // ---- data -------------------------------------------------------------------
+// Auto-classify a .md file that isn't in the manifest yet.
+function autoClassify(slug, body) {
+  const low = (body || '').toLowerCase();
+  // reversible heuristics
+  const reversible = /reversible|dry.?run|rollback|staged|undo|no.?prod.?write|read.?only/.test(low)
+    && !/irreversible|destructive|cannot.?undo|permanent/.test(low);
+  // gate heuristics
+  let gate = 'deploy / prod write';
+  if (/dns|domain|nameserver|cloudflare zone/.test(low)) gate = 'DNS / domain';
+  else if (/spend|budget|stripe|charge|payment|paid api/.test(low)) gate = 'spend';
+  else if (/send.*(email|sms|push|list)|mail.*(blast|send)/.test(low)) gate = 'send (comms-law — NEVER auto)';
+  else if (/dw_unified.*canonical|canonical.*dw_unified/.test(low)) gate = 'dw_unified canonical';
+  else if (/shopify|publish|live store|customer.?facing/.test(low)) gate = 'deploy / prod write';
+  // officer heuristic from slug/body
+  const officer = /commerce|shopify|gmc|merchant|catalog/.test(low) ? 'vp-dw-commerce'
+    : /marketing|instagram|tiktok|social|seo/.test(low) ? 'vp-dw-marketing'
+    : /engineer|code|build|deploy|server/.test(low) ? 'vp-engineering'
+    : 'unassigned';
+  // title: first H1 or first line
+  const h1 = (body || '').match(/^#\s+(.+)/m);
+  const title = h1 ? h1[1].replace(/\*+/g, '').trim() : slug;
+  return { slug, title, officer, state: 'PENDING', signoff: '—', dtd: '—', gate, reversible };
+}
+
 function loadManifest() {
   const m = JSON.parse(fs.readFileSync(MANIFEST, 'utf8'));
-  return m.drafts.map((d) => {
+  const manifestSlugs = new Set(m.drafts.map((d) => d.slug));
+
+  // Dynamically detect files in pending-approval/ not yet in the manifest.
+  let extra = [];
+  try {
+    const files = fs.readdirSync(PENDING).filter((f) => f.endsWith('.md'));
+    for (const f of files) {
+      const slug = f.slice(0, -3); // strip .md
+      if (!manifestSlugs.has(slug)) {
+        let body = '';
+        try { body = fs.readFileSync(path.join(PENDING, f), 'utf8'); } catch (e) { /* ignore */ }
+        extra.push(autoClassify(slug, body));
+      }
+    }
+  } catch (e) { /* ignore */ }
+
+  return [...m.drafts, ...extra].map((d) => {
     const item = { ...d };
     // find the backing .md across queue folders (state may have changed)
     const candidates = [

← 4781118 viewer: add reversible fast-track tray (Approve all N) + per  ·  back to Approval Command Center  ·  nav-agent: universal grid-controls drop-in on internal dashb a809adc →