[object Object]

← back to Ticket System

TK-11946: hold parked tickets out of the DEFAULT / board.html active queue

188325df35d6849680662babe459c437fcfd1caa · 2026-09-20 11:41:03 -0700 · Steve

The parked-lane peel-out only happened in page() at /kanban; the default /
route serves board.html (table view) which was parked-blind, so a parked
active ticket still showed in its open/doing/blocked section and counts —
defeating 'durably held out of the active queue' on the surface Steve defaults to.
withRanking now stamps t.parked onto every /api/tickets row; board.html holds
parked tickets out of active-status sections and relocates them to a 'parked'
group when grouping by status (mirrors the /kanban lane). Verified headless:
parked TK-16 absent from the doing section, control non-parked ticket unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 188325df35d6849680662babe459c437fcfd1caa
Author: Steve <steve@designerwallcoverings.com>
Date:   Sun Sep 20 11:41:03 2026 -0700

    TK-11946: hold parked tickets out of the DEFAULT / board.html active queue
    
    The parked-lane peel-out only happened in page() at /kanban; the default /
    route serves board.html (table view) which was parked-blind, so a parked
    active ticket still showed in its open/doing/blocked section and counts —
    defeating 'durably held out of the active queue' on the surface Steve defaults to.
    withRanking now stamps t.parked onto every /api/tickets row; board.html holds
    parked tickets out of active-status sections and relocates them to a 'parked'
    group when grouping by status (mirrors the /kanban lane). Verified headless:
    parked TK-16 absent from the doing section, control non-parked ticket unaffected.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 board.html | 6 +++---
 server.js  | 6 +++++-
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/board.html b/board.html
index 2c849b80..1c01a272 100644
--- a/board.html
+++ b/board.html
@@ -292,7 +292,7 @@ const DW_KW=/wallcover|wallpaper/i;                                    // DW wal
 const DW_VENDOR=/(^|[^a-z])(sanderson|stroheim|muralsource|kravet|greenland|malibu|gracie|astek|fabricut|groundworks|rebel-walls|thibaut|schumacher|koroseal|scalamandre|maya-?romanoff|winfield|phillip-?jeffries|brewster|wallquest|carnegie|designtex|momentum|versa|elitis|fromental|fentucci|hollywood|novasuede|philipperomano|mdc)($|[^a-z])/i;
 const DW_EXTRA=new Set(['norma','greenland-onboard','sample-followup','sample-followup-sweep','interiordesignershowroom','marketing-command-center','all-designerwallcoverings','consulting-designerwallcoverings-com','mfr-review-viewer','mfr-review-viewer-corruption','designersalesreps','vendor-command-center','discontinued-agent']);
 function isDW(t){const p=String(t.project||'').toLowerCase().trim();if(!p||DW_DENY.has(p))return false;return DW_EXTRA.has(p)||DW_RE.test(p)||DW_VENDOR.test(p)||DW_KW.test(p);}
-function match(t){if(VIEW!=='all'&&(t.kind||'task')!==VIEW)return false;if(['open','blocked','doing'].includes(SECTION)&&t.status!==SECTION)return false;if(SECTION==='nondw'&&isDW(t))return false;if(!q)return true;
+function match(t){if(VIEW!=='all'&&(t.kind||'task')!==VIEW)return false;if(['open','blocked','doing'].includes(SECTION)&&t.status!==SECTION)return false;if(['open','blocked','doing'].includes(SECTION)&&t.parked)return false;/*TK-11946: parked held out of the active queue (mirrors /kanban parked lane)*/if(SECTION==='nondw'&&isDW(t))return false;if(!q)return true;
   // search the FULL thread — every comment (note/win/challenge/cody incl.) + every action — not just lastText
   const thread=[...(t.comments||[]).map(c=>c.text),...(t.actions||[]).map(a=>a.text)].join(' ');
   const hay=(t.id+' '+t.title+' '+(t.project||'')+' '+(t.assignee||'')+' '+t.status+' '+(t.kind||'task')+' '+JSON.stringify(t.schedule||{})+' '+thread).toLowerCase();
@@ -328,9 +328,9 @@ function render(){
     for(const t of rows)rowTr(t);
   }else{
     const groups=new Map();
-    for(const t of rows){const gv=GROUP==='status'?t.status:GROUP==='blocker'?(t.blocker?.type||''):GROUP==='project'?(t.project||''):(t.assignee||'');(groups.get(gv)||groups.set(gv,[]).get(gv)).push(t);}
+    for(const t of rows){const gv=GROUP==='status'?((t.parked&&['open','doing','blocked'].includes(t.status))?'parked':t.status)/*TK-11946*/:GROUP==='blocker'?(t.blocker?.type||''):GROUP==='project'?(t.project||''):(t.assignee||'');(groups.get(gv)||groups.set(gv,[]).get(gv)).push(t);}
     // order groups: for status use canonical order, else alpha
-    const order=GROUP==='status'?['open','doing','blocked','done','stopped']:[...groups.keys()].sort((a,b)=>String(a).localeCompare(String(b)));
+    const order=GROUP==='status'?['open','doing','blocked','done','stopped','parked']:[...groups.keys()].sort((a,b)=>String(a).localeCompare(String(b)));
     for(const gv of order){const list=groups.get(gv);if(!list)continue;
       const gkey=GROUP+':'+gv; const collapsed=COLLAPSED.has(gkey);
       const allSel=list.every(t=>SEL.has(t.id))&&list.length>0;
diff --git a/server.js b/server.js
index 8a81aebe..fa29ade8 100644
--- a/server.js
+++ b/server.js
@@ -458,7 +458,11 @@ function withRanking(list) {
   const parked = parkedTicketIds();
   const scored = [];
   for (const t of list) {
-    if ((t.kind || 'task') === 'task' && RANKABLE.has(t.status) && !parked.has(shortTk(t.id))) {
+    // TK-11946: stamp the durable parked flag onto every ticket so the DEFAULT `/`
+    // table view (board.html, which reads /api/tickets) can hold a parked ticket OUT
+    // of the active queue — the /kanban parked-lane peel-out was the only surface honoring it.
+    t.parked = parked.has(shortTk(t.id));
+    if ((t.kind || 'task') === 'task' && RANKABLE.has(t.status) && !t.parked) {
       const s = scoreTicket(t);
       t.priority = s.priority; t.tier = s.tier;
       t.ratings = { value: s.value, urgency: s.urgency, ease: s.ease, safety: s.safety };

← e8b78db9 ticket board: PARKED lane + reaper skips parked tickets (dur  ·  back to Ticket System  ·  TK-11951: needs-Steve tab backlog triage/fire-list (6 tabs a 3ca99fae →