…
```embedhtml
<!--
- d2 pipeline page header map (designer-built, Razie approved 2026-09-28).
+ d2 pipeline page header map (designer-built, Razie approved 2026-09-28; roll-up, look and dark you-initial 2026-09-28 evening). Also the Agents page's diagram (2026-09-28): pass waits.
Drop in as is. Markup: wrap the page's lede paragraph and an empty <div class="pm-map"></div>
in <div class="pm-row">, then call d2PipeMap(el, data) with:
…
items simple: {new, 'in-progress', review, waiting} over the whole list
complex: {designer:{...}, coder:{...}, tester:{...}, guardian:{...}} per role, person + agent roles
- agents latest board state per agent role name: {maker, designer, coder, tester, guardian} (null = none)
+ agents board states per role: {maker, designer, coder, tester, guardian}; each a state string, or an array of
+ states when several agents report under the role (null = none). Agents with other roles are not drawn:
+ the map shows the known roles only (Razie 2026-09-28); the Agents page list shows every agent.
+ Several live agents on a role roll up into one circle: the count inside, a pie of their state colours,
+ the plain role name under it. done and gone agents are left out of the roll-up while any other is live.
+ look optional: number of items to look at (^pipe-45): "N to look at" under the you node, to ?look=1
+ ctx optional: {<role>: <context % the agent last reported>} draws a ring around its node
+ (orange from 70) and adds " · N%" to its state line; for a rolled-up role, the fullest agent's
+ waits optional (Agents page): [{from:'coder', to:'you'|'designer'|…}] from each agent's waitingOn;
+ drawn as a dashed amber arc under the row, from the waiting agent back to whom it waits on
+ Agents page (Razie 2026-09-28): mode 'agents' with
+ roles every role that reports (known ones first, in pipeline order), instead of the known roles only;
+ roles outside the pipeline (e.g. dispatcher) come after a dotted divider, with no arrow or item pills
+ list {<role>: [{agent, state, ctx?, item?, text?}]} every agent on the board, by role
+ loadLog(f) f = {role} or {agent}; returns a Promise of [{at, agent, state, text, item?}], newest first
+ (Log:AgentHistory plus the current statuses; the page's one data call)
+ Tapping a role with several agents opens a second row under the map: an "All (n)" chip (the role's merged log,
+ selected first) and one chip per agent; tapping a chip shows that log. A role with one agent goes straight
+ to its log. Tapping the same role again closes the panel.
The one wiring point: STATE below must be the Agents page's own state-colour map; change it there, not here.
-->
…
.pm-map .pm-n:hover circle,.pm-map .pm-n:focus circle{stroke:var(--ink,#1d1d1b);stroke-width:2}
.pm-map text{font-family:inherit}
+.pm-map .pm-you{fill:#633806}
+@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .pm-map .pm-you{fill:#fbe3b0}}
+:root[data-theme="dark"] .pm-map .pm-you{fill:#fbe3b0}
+.pm-det{margin-top:8px;border-top:1px solid var(--line,#2c3039);padding-top:8px;font-size:13px}
+.pm-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
+.pm-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line,#2c3039);border-radius:14px;padding:3px 10px 3px 6px;background:none;color:var(--ink,#1d1d1b);font:inherit;font-size:12px;cursor:pointer}
+.pm-chip[aria-pressed=true]{border-color:var(--ink,#1d1d1b);font-weight:600}
+.pm-dot{width:10px;height:10px;border-radius:50%;display:inline-block;flex:none}
+.pm-log{list-style:none;margin:0;padding:0;max-height:260px;overflow:auto}
+.pm-log li{display:flex;gap:8px;align-items:baseline;padding:3px 0;border-bottom:1px solid var(--line,#2c3039)}
+.pm-log .t{color:var(--soft,#6b6a66);font-variant-numeric:tabular-nums;flex:none;font-size:12px}
+.pm-log .s{flex:none;font-size:11px;border-radius:8px;padding:0 6px;color:#fff}
+.pm-log .a{flex:none;font-size:12px;color:var(--soft,#6b6a66)}
+.pm-log .x{flex:1;min-width:0;overflow-wrap:anywhere}
+.pm-log a{color:inherit;text-decoration:underline}
.pm-help{display:block;text-align:center;font-size:12px;color:var(--soft,#6b6a66);margin-top:2px}
</style>
<script>
(function(){
-var STATE={"up":"#6b7fd7","idle":"#9aa3b2","working":"#2f9a6f","waiting":"#d9a520","done":"#1f6b4c","stuck":"#c0392b","gone":"#555c66"};
+var STATE={up:'#5a8fc7',idle:'#5a8fc7',working:'#2f9a6f',waiting:'#d9a520','handing-over':'#8a6fc7',done:'#9c9a93',stuck:'#c9483b',gone:'#9c9a93',warning:'#e07a2e',stopped:'#c9483b'};
var DARKTEXT={waiting:1};
-var ITEM=[['new','var(--soft,#9c9a93)'],['in-progress','#2f9a6f'],['waiting','#d9a520'],['review','var(--attn,#e07a2e)']];
+var ITEM=[['new','var(--soft,#9c9a93)'],['in-progress','#2f9a6f'],['waiting','#d9a520'],['review','#7a7fc9']];
var NS='http://www.w3.org/2000/svg';
function el(n,a,t){var e=document.createElementNS(NS,n);for(var k in a)e.setAttribute(k,a[k]);if(t!=null)e.textContent=t;return e}
…
p.appendChild(el('text',{x:x+w/2,y:y+11,'text-anchor':'middle','font-size':11,fill:'#fff'},String(c[s[0]])));
p.appendChild(el('title',{},c[s[0]]+' '+s[0]));g.appendChild(p);x+=w+gap});}
-function node(svg,x,y,r,label,sub,state,href,isYou,initial){
+var RANK=['stuck','stopped','warning','waiting','handing-over','working','up','idle','done','gone'];
+function roll(v){if(v==null)return null;var a=Array.isArray(v)?v.slice():[v];if(!a.length)return null;
+ var live=a.filter(function(s){return s!=='done'&&s!=='gone'});if(live.length)a=live;else a=[a.sort(function(p,q){return RANK.indexOf(p)-RANK.indexOf(q)})[0]];
+ var c={};a.forEach(function(s){c[s]=(c[s]||0)+1});var ks=Object.keys(c).sort(function(p,q){return RANK.indexOf(p)-RANK.indexOf(q)});
+ return {n:a.length,top:ks[0],counts:c,keys:ks,sub:a.length===1?ks[0]:c[ks[0]]+' '+ks[0]+(a.length>c[ks[0]]?' +'+(a.length-c[ks[0]]):'')}}
+function pie(a,x,y,r,R){var ang=-Math.PI/2;R.keys.forEach(function(k){var f=R.counts[k]/R.n,e=ang+f*2*Math.PI;
+ if(f>=1){a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:STATE[k]||'#9c9a93'}));return}
+ var x1=x+r*Math.cos(ang),y1=y+r*Math.sin(ang),x2=x+r*Math.cos(e),y2=y+r*Math.sin(e);
+ var p=el('path',{d:'M'+x+' '+y+'L'+x1.toFixed(2)+' '+y1.toFixed(2)+'A'+r+' '+r+' 0 '+(f>0.5?1:0)+' 1 '+x2.toFixed(2)+' '+y2.toFixed(2)+'Z',fill:STATE[k]||'#9c9a93',stroke:'var(--bg,#fff)','stroke-width':1});
+ p.appendChild(el('title',{},R.counts[k]+' '+k));a.appendChild(p);ang=e});}
+function node(svg,x,y,r,label,sub,state,href,isYou,initial,R){
var a=el('a',{href:href,class:'pm-n'});a.appendChild(el('title',{},label+(state?': '+state:'')));
if(isYou)a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:'var(--sun-bg,#fbf1dc)',stroke:'var(--sun,#d99a2b)','stroke-width':1.5}));
+ else if(R&&R.n>1)pie(a,x,y,r,R);
else if(state)a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:STATE[state]||'#9c9a93'}));
else a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:'none',stroke:'var(--soft,#9c9a93)','stroke-width':1.5,'stroke-dasharray':'3 3'}));
- var ink=isYou?'#633806':(state?(DARKTEXT[state]?'#412402':'#fff'):'var(--soft,#6b6a66)');
- a.appendChild(el('text',{x:x,y:y+4,'text-anchor':'middle','font-size':12,fill:ink},initial));
+ var multi=R&&R.n>1,ink=multi?'#fff':(state?(DARKTEXT[state]?'#412402':'#fff'):'var(--soft,#6b6a66)');
+ var it=el('text',isYou?{x:x,y:y+4,'text-anchor':'middle','font-size':12,class:'pm-you'}:{x:x,y:y+(multi?5:4),'text-anchor':'middle','font-size':multi?14:12,'font-weight':multi?600:400,fill:ink,stroke:multi?'rgba(0,0,0,.35)':'none','stroke-width':multi?2:0,'paint-order':'stroke'},multi?String(R.n):initial);a.appendChild(it);
a.appendChild(el('text',{x:x,y:y+r+20,'text-anchor':'middle','font-size':12,fill:'var(--ink,#1d1d1b)'},label));
a.appendChild(el('text',{x:x,y:y+r+36,'text-anchor':'middle','font-size':11,fill:isYou?'var(--sun,#d99a2b)':(state?(STATE[state]):'var(--soft,#6b6a66)')},sub));
svg.appendChild(a);}
function arrow(svg,x1,x2,y){svg.appendChild(el('path',{d:'M'+x1+' '+y+'H'+(x2-2),stroke:'var(--soft,#9c9a93)','stroke-width':1.5,fill:'none','marker-end':'url(#pm-a)'}));}
+function hm(t){var x=new Date(t);return isNaN(x)?'':('0'+x.getHours()).slice(-2)+':'+('0'+x.getMinutes()).slice(-2)}
+function details(host,svg,d,list){var det=document.createElement('div');det.className='pm-det';det.hidden=true;host.appendChild(det);var open=null;
+ function showLog(f,box){box.innerHTML='<div style="color:var(--soft,#6b6a66)">Loading…</div>';
+ Promise.resolve(d.loadLog?d.loadLog(f):[]).then(function(rows){var ul=document.createElement('ul');ul.className='pm-log';
+ if(!rows||!rows.length){box.innerHTML='<div style="color:var(--soft,#6b6a66)">Nothing logged yet.</div>';return}
+ rows.forEach(function(r){var li=document.createElement('li');
+ var t=document.createElement('span');t.className='t';t.textContent=hm(r.at);li.appendChild(t);
+ var s=document.createElement('span');s.className='s';s.textContent=r.state||'';s.style.background=STATE[r.state]||'#9c9a93';if(DARKTEXT[r.state])s.style.color='#412402';li.appendChild(s);
+ if(f.role){var a=document.createElement('span');a.className='a';a.textContent=r.agent;li.appendChild(a)}
+ var x=document.createElement('span');x.className='x';x.textContent=(r.text||'')+(r.item?' ':'');if(r.item){var il=document.createElement('a');il.href='/pipeline/'+r.item;il.textContent=r.item;x.appendChild(il)}li.appendChild(x);ul.appendChild(li)});
+ box.innerHTML='';box.appendChild(ul)},function(){box.textContent='Could not load the log.'})}
+ function chip(label,state,pressed,onclick){var b=document.createElement('button');b.type='button';b.className='pm-chip';b.setAttribute('aria-pressed',pressed?'true':'false');
+ var dot=document.createElement('span');dot.className='pm-dot';dot.style.background=state?(STATE[state]||'#9c9a93'):'var(--soft,#9c9a93)';b.appendChild(dot);b.appendChild(document.createTextNode(label));b.onclick=onclick;return b}
+ svg.querySelectorAll('a.pm-n').forEach(function(a){var h=a.getAttribute('href')||'';if(h.indexOf('#r=')!==0)return;var role=decodeURIComponent(h.slice(3));
+ a.addEventListener('click',function(e){e.preventDefault();if(open===role){det.hidden=true;open=null;return}open=role;det.hidden=false;det.innerHTML='';
+ var ags=list[role]||[],box=document.createElement('div');
+ if(ags.length>1){var row=document.createElement('div');row.className='pm-chips';var R=roll(ags.map(function(x){return x.state}));
+ var press=function(b){row.querySelectorAll('.pm-chip').forEach(function(x){x.setAttribute('aria-pressed',x===b?'true':'false')})};
+ var all=chip('All '+role+' ('+ags.length+')',R&&R.top,true,function(){press(all);showLog({role:role},box)});row.appendChild(all);
+ ags.forEach(function(g){var c=chip(g.agent+' · '+g.state+(g.ctx!=null?' · '+Math.round(g.ctx)+'%':''),g.state,false,function(){press(c);showLog({agent:g.agent},box)});c.title=(g.text||'')+(g.item?' ('+g.item+')':'');row.appendChild(c)});
+ det.appendChild(row);det.appendChild(box);showLog({role:role},box)}
+ else{var g=ags[0];if(g){var hd=document.createElement('div');hd.className='pm-chips';hd.appendChild(chip(g.agent+' · '+g.state+(g.ctx!=null?' · '+Math.round(g.ctx)+'%':''),g.state,true,function(){}));det.appendChild(hd)}
+ det.appendChild(box);if(g)showLog({agent:g.agent},box);else box.textContent='No agent on this role yet.'}
+ })})}
window.d2PipeMap=function(host,d){
host.innerHTML='';var complex=d.view==='complex',ag=d.agents||{},you=d.you||0;
- var roles=complex?['designer','coder'].concat(ag.tester==='working'?['tester']:[]).concat(['guardian']):[];
- var W=complex?90+roles.length*95:300,Y=52,r=complex?21:24;
- var svg=el('svg',{viewBox:'0 0 '+W+' 120',role:'img'});
+ var agentsMode=d.mode==='agents',list=d.list||{};
+ if(agentsMode){ag={};Object.keys(list).forEach(function(k){ag[k]=list[k].map(function(x){return x.state})});d.ctx=d.ctx||{};Object.keys(list).forEach(function(k){var c=list[k].map(function(x){return x.ctx}).filter(function(v){return v!=null});if(c.length)d.ctx[k]=c})}
+ var roles=agentsMode&&d.roles?d.roles.slice():complex?['designer','coder'].concat(ag.tester==='working'?['tester']:[]).concat(['guardian']):[];
+ var W=complex?90+roles.length*95:300,Y=52,r=complex?21:24,look=function(svg,x,y){if(!d.look)return;var a=el('a',{href:'?look=1'});
+ a.appendChild(el('text',{x:x,y:y,'text-anchor':'middle','font-size':11,fill:'var(--attn,#7a7fc9)'},d.look+' to look at'));svg.appendChild(a)};
+ var svg=el('svg',{viewBox:'0 0 '+W+' '+(d.look?130:120),role:'img'});
svg.appendChild(el('title',{},'How work flows: you, '+(complex?roles.join(', '):'the pipeline, your AI')));
var defs=el('defs',{}),m=el('marker',{id:'pm-a',viewBox:'0 0 10 10',refX:8,refY:5,markerWidth:6,markerHeight:6,orient:'auto'});
…
var youSub=you?you+' for you':'nothing for you';
if(!complex){
- node(svg,38,Y,r,'you',youSub,null,'?who=mine',true,'you');
+ node(svg,38,Y,r,'you',youSub,null,'?who=mine',true,'you');look(svg,38,Y+r+50);
arrow(svg,64,100,Y);
var box=el('g',{});box.appendChild(el('rect',{x:102,y:Y-22,width:96,height:44,rx:10,fill:'none',stroke:'var(--soft,#9c9a93)','stroke-dasharray':'4 3'}));
…
pills(box,150,Y+1,d.items||{});svg.appendChild(box);
arrow(svg,200,236,Y);
- var st=ag.maker||null;node(svg,262,Y,r,'AI',st||'not started',st,'?role=maker',false,'AI');
+ var RM=roll(ag.maker),st=RM?RM.top:null;node(svg,262,Y,r,'AI',RM?RM.sub:'not started',st,'?role=maker',false,'AI',RM);
} else {
- node(svg,32,Y,r,'you',youSub,null,'?who=mine',true,'you');
- var x=32;roles.forEach(function(role){var nx=x+95;arrow(svg,x+r+2,nx-r,Y);
- pills(svg,(x+nx)/2,Y-38,(d.items||{})[role]||{});
- var s=ag[role]||null;node(svg,nx,Y,r,role,s||'not started',s,'?role='+encodeURIComponent(role),false,role.charAt(0).toUpperCase());x=nx;});
+ node(svg,32,Y,r,'you',youSub,null,'?who=mine',true,'you');look(svg,32,Y+r+50);
+ var KNOWN=['designer','coder','tester','guardian'],sep=false;
+ var x=32;roles.forEach(function(role){var nx=x+95,cxs=d.ctx||{},known=KNOWN.indexOf(role)>=0;
+ if(!known&&!sep){sep=true;svg.appendChild(el('path',{d:'M'+(x+47)+' '+(Y-26)+'V'+(Y+26),stroke:'var(--line,#2c3039)','stroke-width':1.5,'stroke-dasharray':'2 3'}))}
+ if(known)arrow(svg,x+r+2+(cxs[x===32?'':roles[roles.indexOf(role)-1]]!=null?7:0),nx-r-(cxs[role]!=null?8:0),Y);
+ if(known)pills(svg,(x+nx)/2,Y-38,(d.items||{})[role]||{});
+ var R=roll(ag[role]),s=R?R.top:null,cx=(d.ctx||{})[role];if(Array.isArray(cx))cx=cx.length?Math.max.apply(null,cx):null;
+ if(s&&cx!=null){var RR=r+6,C=2*Math.PI*RR,pct=Math.max(0,Math.min(100,cx));
+ svg.appendChild(el('circle',{cx:nx,cy:Y,r:RR,fill:'none',stroke:'var(--line,#2c3039)','stroke-width':4}));
+ var ring=el('circle',{cx:nx,cy:Y,r:RR,fill:'none',stroke:pct>=70?'#e07a2e':'#5a8fc7','stroke-width':4,'stroke-linecap':'round','stroke-dasharray':(C*pct/100).toFixed(1)+' '+C.toFixed(1),transform:'rotate(-90 '+nx+' '+Y+')'});
+ ring.appendChild(el('title',{},role+': context ~'+Math.round(pct)+'%'));svg.appendChild(ring);}
+ node(svg,nx,Y,r,role,(R?R.sub:'not started')+(s&&cx!=null&&R.n===1?' · '+Math.round(cx)+'%':''),s,(agentsMode?'#r='+encodeURIComponent(role):'?role='+encodeURIComponent(role)),false,role.charAt(0).toUpperCase(),R);x=nx;});
}
+ if(complex&&d.waits&&d.waits.length){var X={you:32};var xx=32;roles.forEach(function(ro){xx+=95;X[ro]=xx});
+ var dm=el('marker',{id:'pm-w',viewBox:'0 0 10 10',refX:8,refY:5,markerWidth:6,markerHeight:6,orient:'auto'});dm.appendChild(el('path',{d:'M0 0L10 5L0 10z',fill:'#d9a520'}));defs.appendChild(dm);
+ d.waits.forEach(function(w,i){var a=X[w.from],b=X[w.to];if(a==null||b==null||a===b)return;var o=d.look?10:0,yb=Y+r+44+o+i*10,dip=yb+14;
+ var p=el('path',{d:'M'+a+' '+(Y+r+40+o)+' C'+a+' '+dip+' '+b+' '+dip+' '+b+' '+(Y+r+40+o),stroke:'#d9a520','stroke-width':1.5,'stroke-dasharray':'4 3',fill:'none','marker-end':'url(#pm-w)'});
+ p.appendChild(el('title',{},w.from+' is waiting on '+w.to));svg.appendChild(p);});
+ svg.setAttribute('viewBox','0 0 '+W+' '+(140+d.waits.length*10+(d.look?10:0)));}
host.appendChild(svg);
+ if(agentsMode)details(host,svg,d,list);
if(d.help){var h=document.createElement('a');h.className='pm-help';h.href=d.help;h.textContent='How it works ›';host.appendChild(h);}
};
…
(function(){const root=document.getElementById("d2pipe-master");if(!root)return;
const P={
-you:["You",["Park an idea: tell your AI \"park it\" and it makes the item, with your words","Take an item and work on it; set its status; mark it done","Reassign it to another person or to one of your agent roles","Hand it on to the next role (spec → design → code → test)","Answer an item waiting on you: Approve or Refuse","Make a follow-up when someone else took it","Drop it, saying why; move it to another of your projects","Your own items stand out and come first"]],
+you:["You",["Park an idea: tell your AI \"park it\" and it makes the item, with your words, and parks it","Take an item and work on it; set its status; mark it done","Reassign it to another person or to one of your agent roles","Hand it on to the next role (spec → design → code → test)","Answer an item waiting on you: Approve or Refuse","Make a follow-up when someone else took it","Drop it, saying why; move it to another of your projects","Your own items stand out and come first"]],
others:["Members",["Everyone on the project sees every item","They take and finish what's for them","Work reaches them when a person passes it on","They get a notice when something is theirs or waits on them"]],
new:["New",["A title, from → for, and a document that starts with the original words","Can wait on other items; it unlocks when they're done","Anyone can still edit, reassign or drop it"]],
…
A **guardian** agent watches the changes in its areas and reports what it finds, as items for whoever made the change; it never fixes another role's work (being designed). How to run agents, guardians included: [[Help:Agents]].
-**The map at the top of your Pipeline page** (pro and master projects; sample data here): you, then each role in turn (designer, coder, the tester while it's testing, the guardian, then any other role your agents report or your open items are for), each coloured by what its agent is doing and topped with its items by status; when two agents share a role, each has its own circle, named. Tap a role to see its items (and its agent's).
+**The map at the top of your Pipeline page** (pro and master projects; sample data here): you, then each role in turn (designer, coder, the tester while it's testing, the guardian), each coloured by what its agent is doing and topped with its items by status; when several agents work in one role, they share its circle: the count inside, a slice of colour for each one's state. On master projects the Agents page lists every agent, whatever its role. Tap a role to see its items (and its agent's).
```embedhtml
<!--
- d2 pipeline page header map (designer-built, Razie approved 2026-09-28).
+ d2 pipeline page header map (designer-built, Razie approved 2026-09-28; roll-up, look and dark you-initial 2026-09-28 evening). Also the Agents page's diagram (2026-09-28): pass waits.
Drop in as is. Markup: wrap the page's lede paragraph and an empty <div class="pm-map"></div>
in <div class="pm-row">, then call d2PipeMap(el, data) with:
…
items simple: {new, 'in-progress', review, waiting} over the whole list
complex: {designer:{...}, coder:{...}, tester:{...}, guardian:{...}} per role, person + agent roles
- agents latest board state per agent role name: {maker, designer, coder, tester, guardian} (null = none)
+ agents board states per role: {maker, designer, coder, tester, guardian}; each a state string, or an array of
+ states when several agents report under the role (null = none). Agents with other roles are not drawn:
+ the map shows the known roles only (Razie 2026-09-28); the Agents page list shows every agent.
+ Several live agents on a role roll up into one circle: the count inside, a pie of their state colours,
+ the plain role name under it. done and gone agents are left out of the roll-up while any other is live.
+ look optional: number of items to look at (^pipe-45): "N to look at" under the you node, to ?look=1
+ ctx optional: {<role>: <context % the agent last reported>} draws a ring around its node
+ (orange from 70) and adds " · N%" to its state line; for a rolled-up role, the fullest agent's
+ waits optional (Agents page): [{from:'coder', to:'you'|'designer'|…}] from each agent's waitingOn;
+ drawn as a dashed amber arc under the row, from the waiting agent back to whom it waits on
+ Agents page (Razie 2026-09-28): mode 'agents' with
+ roles every role that reports (known ones first, in pipeline order), instead of the known roles only;
+ roles outside the pipeline (e.g. dispatcher) come after a dotted divider, with no arrow or item pills
+ list {<role>: [{agent, state, ctx?, item?, text?}]} every agent on the board, by role
+ loadLog(f) f = {role} or {agent}; returns a Promise of [{at, agent, state, text, item?}], newest first
+ (Log:AgentHistory plus the current statuses; the page's one data call)
+ Tapping a role with several agents opens a second row under the map: an "All (n)" chip (the role's merged log,
+ selected first) and one chip per agent; tapping a chip shows that log. A role with one agent goes straight
+ to its log. Tapping the same role again closes the panel.
The one wiring point: STATE below must be the Agents page's own state-colour map; change it there, not here.
-->
…
.pm-map .pm-n:hover circle,.pm-map .pm-n:focus circle{stroke:var(--ink,#1d1d1b);stroke-width:2}
.pm-map text{font-family:inherit}
+.pm-map .pm-you{fill:#633806}
+@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .pm-map .pm-you{fill:#fbe3b0}}
+:root[data-theme="dark"] .pm-map .pm-you{fill:#fbe3b0}
+.pm-det{margin-top:8px;border-top:1px solid var(--line,#2c3039);padding-top:8px;font-size:13px}
+.pm-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}
+.pm-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line,#2c3039);border-radius:14px;padding:3px 10px 3px 6px;background:none;color:var(--ink,#1d1d1b);font:inherit;font-size:12px;cursor:pointer}
+.pm-chip[aria-pressed=true]{border-color:var(--ink,#1d1d1b);font-weight:600}
+.pm-dot{width:10px;height:10px;border-radius:50%;display:inline-block;flex:none}
+.pm-log{list-style:none;margin:0;padding:0;max-height:260px;overflow:auto}
+.pm-log li{display:flex;gap:8px;align-items:baseline;padding:3px 0;border-bottom:1px solid var(--line,#2c3039)}
+.pm-log .t{color:var(--soft,#6b6a66);font-variant-numeric:tabular-nums;flex:none;font-size:12px}
+.pm-log .s{flex:none;font-size:11px;border-radius:8px;padding:0 6px;color:#fff}
+.pm-log .a{flex:none;font-size:12px;color:var(--soft,#6b6a66)}
+.pm-log .x{flex:1;min-width:0;overflow-wrap:anywhere}
+.pm-log a{color:inherit;text-decoration:underline}
.pm-help{display:block;text-align:center;font-size:12px;color:var(--soft,#6b6a66);margin-top:2px}
</style>
<script>
(function(){
-var STATE={"up":"#6b7fd7","idle":"#9aa3b2","working":"#2f9a6f","waiting":"#d9a520","done":"#1f6b4c","stuck":"#c0392b","gone":"#555c66"};
+var STATE={up:'#5a8fc7',idle:'#5a8fc7',working:'#2f9a6f',waiting:'#d9a520','handing-over':'#8a6fc7',done:'#9c9a93',stuck:'#c9483b',gone:'#9c9a93',warning:'#e07a2e',stopped:'#c9483b'};
var DARKTEXT={waiting:1};
-var ITEM=[['new','var(--soft,#9c9a93)'],['in-progress','#2f9a6f'],['waiting','#d9a520'],['review','var(--attn,#e07a2e)']];
+var ITEM=[['new','var(--soft,#9c9a93)'],['in-progress','#2f9a6f'],['waiting','#d9a520'],['review','#7a7fc9']];
var NS='http://www.w3.org/2000/svg';
function el(n,a,t){var e=document.createElementNS(NS,n);for(var k in a)e.setAttribute(k,a[k]);if(t!=null)e.textContent=t;return e}
…
p.appendChild(el('text',{x:x+w/2,y:y+11,'text-anchor':'middle','font-size':11,fill:'#fff'},String(c[s[0]])));
p.appendChild(el('title',{},c[s[0]]+' '+s[0]));g.appendChild(p);x+=w+gap});}
-function node(svg,x,y,r,label,sub,state,href,isYou,initial){
+var RANK=['stuck','stopped','warning','waiting','handing-over','working','up','idle','done','gone'];
+function roll(v){if(v==null)return null;var a=Array.isArray(v)?v.slice():[v];if(!a.length)return null;
+ var live=a.filter(function(s){return s!=='done'&&s!=='gone'});if(live.length)a=live;else a=[a.sort(function(p,q){return RANK.indexOf(p)-RANK.indexOf(q)})[0]];
+ var c={};a.forEach(function(s){c[s]=(c[s]||0)+1});var ks=Object.keys(c).sort(function(p,q){return RANK.indexOf(p)-RANK.indexOf(q)});
+ return {n:a.length,top:ks[0],counts:c,keys:ks,sub:a.length===1?ks[0]:c[ks[0]]+' '+ks[0]+(a.length>c[ks[0]]?' +'+(a.length-c[ks[0]]):'')}}
+function pie(a,x,y,r,R){var ang=-Math.PI/2;R.keys.forEach(function(k){var f=R.counts[k]/R.n,e=ang+f*2*Math.PI;
+ if(f>=1){a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:STATE[k]||'#9c9a93'}));return}
+ var x1=x+r*Math.cos(ang),y1=y+r*Math.sin(ang),x2=x+r*Math.cos(e),y2=y+r*Math.sin(e);
+ var p=el('path',{d:'M'+x+' '+y+'L'+x1.toFixed(2)+' '+y1.toFixed(2)+'A'+r+' '+r+' 0 '+(f>0.5?1:0)+' 1 '+x2.toFixed(2)+' '+y2.toFixed(2)+'Z',fill:STATE[k]||'#9c9a93',stroke:'var(--bg,#fff)','stroke-width':1});
+ p.appendChild(el('title',{},R.counts[k]+' '+k));a.appendChild(p);ang=e});}
+function node(svg,x,y,r,label,sub,state,href,isYou,initial,R){
var a=el('a',{href:href,class:'pm-n'});a.appendChild(el('title',{},label+(state?': '+state:'')));
if(isYou)a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:'var(--sun-bg,#fbf1dc)',stroke:'var(--sun,#d99a2b)','stroke-width':1.5}));
+ else if(R&&R.n>1)pie(a,x,y,r,R);
else if(state)a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:STATE[state]||'#9c9a93'}));
else a.appendChild(el('circle',{cx:x,cy:y,r:r,fill:'none',stroke:'var(--soft,#9c9a93)','stroke-width':1.5,'stroke-dasharray':'3 3'}));
- var ink=isYou?'#633806':(state?(DARKTEXT[state]?'#412402':'#fff'):'var(--soft,#6b6a66)');
- a.appendChild(el('text',{x:x,y:y+4,'text-anchor':'middle','font-size':12,fill:ink},initial));
+ var multi=R&&R.n>1,ink=multi?'#fff':(state?(DARKTEXT[state]?'#412402':'#fff'):'var(--soft,#6b6a66)');
+ var it=el('text',isYou?{x:x,y:y+4,'text-anchor':'middle','font-size':12,class:'pm-you'}:{x:x,y:y+(multi?5:4),'text-anchor':'middle','font-size':multi?14:12,'font-weight':multi?600:400,fill:ink,stroke:multi?'rgba(0,0,0,.35)':'none','stroke-width':multi?2:0,'paint-order':'stroke'},multi?String(R.n):initial);a.appendChild(it);
a.appendChild(el('text',{x:x,y:y+r+20,'text-anchor':'middle','font-size':12,fill:'var(--ink,#1d1d1b)'},label));
a.appendChild(el('text',{x:x,y:y+r+36,'text-anchor':'middle','font-size':11,fill:isYou?'var(--sun,#d99a2b)':(state?(STATE[state]):'var(--soft,#6b6a66)')},sub));
svg.appendChild(a);}
function arrow(svg,x1,x2,y){svg.appendChild(el('path',{d:'M'+x1+' '+y+'H'+(x2-2),stroke:'var(--soft,#9c9a93)','stroke-width':1.5,fill:'none','marker-end':'url(#pm-a)'}));}
+function hm(t){var x=new Date(t);return isNaN(x)?'':('0'+x.getHours()).slice(-2)+':'+('0'+x.getMinutes()).slice(-2)}
+function details(host,svg,d,list){var det=document.createElement('div');det.className='pm-det';det.hidden=true;host.appendChild(det);var open=null;
+ function showLog(f,box){box.innerHTML='<div style="color:var(--soft,#6b6a66)">Loading…</div>';
+ Promise.resolve(d.loadLog?d.loadLog(f):[]).then(function(rows){var ul=document.createElement('ul');ul.className='pm-log';
+ if(!rows||!rows.length){box.innerHTML='<div style="color:var(--soft,#6b6a66)">Nothing logged yet.</div>';return}
+ rows.forEach(function(r){var li=document.createElement('li');
+ var t=document.createElement('span');t.className='t';t.textContent=hm(r.at);li.appendChild(t);
+ var s=document.createElement('span');s.className='s';s.textContent=r.state||'';s.style.background=STATE[r.state]||'#9c9a93';if(DARKTEXT[r.state])s.style.color='#412402';li.appendChild(s);
+ if(f.role){var a=document.createElement('span');a.className='a';a.textContent=r.agent;li.appendChild(a)}
+ var x=document.createElement('span');x.className='x';x.textContent=(r.text||'')+(r.item?' ':'');if(r.item){var il=document.createElement('a');il.href='/pipeline/'+r.item;il.textContent=r.item;x.appendChild(il)}li.appendChild(x);ul.appendChild(li)});
+ box.innerHTML='';box.appendChild(ul)},function(){box.textContent='Could not load the log.'})}
+ function chip(label,state,pressed,onclick){var b=document.createElement('button');b.type='button';b.className='pm-chip';b.setAttribute('aria-pressed',pressed?'true':'false');
+ var dot=document.createElement('span');dot.className='pm-dot';dot.style.background=state?(STATE[state]||'#9c9a93'):'var(--soft,#9c9a93)';b.appendChild(dot);b.appendChild(document.createTextNode(label));b.onclick=onclick;return b}
+ svg.querySelectorAll('a.pm-n').forEach(function(a){var h=a.getAttribute('href')||'';if(h.indexOf('#r=')!==0)return;var role=decodeURIComponent(h.slice(3));
+ a.addEventListener('click',function(e){e.preventDefault();if(open===role){det.hidden=true;open=null;return}open=role;det.hidden=false;det.innerHTML='';
+ var ags=list[role]||[],box=document.createElement('div');
+ if(ags.length>1){var row=document.createElement('div');row.className='pm-chips';var R=roll(ags.map(function(x){return x.state}));
+ var press=function(b){row.querySelectorAll('.pm-chip').forEach(function(x){x.setAttribute('aria-pressed',x===b?'true':'false')})};
+ var all=chip('All '+role+' ('+ags.length+')',R&&R.top,true,function(){press(all);showLog({role:role},box)});row.appendChild(all);
+ ags.forEach(function(g){var c=chip(g.agent+' · '+g.state+(g.ctx!=null?' · '+Math.round(g.ctx)+'%':''),g.state,false,function(){press(c);showLog({agent:g.agent},box)});c.title=(g.text||'')+(g.item?' ('+g.item+')':'');row.appendChild(c)});
+ det.appendChild(row);det.appendChild(box);showLog({role:role},box)}
+ else{var g=ags[0];if(g){var hd=document.createElement('div');hd.className='pm-chips';hd.appendChild(chip(g.agent+' · '+g.state+(g.ctx!=null?' · '+Math.round(g.ctx)+'%':''),g.state,true,function(){}));det.appendChild(hd)}
+ det.appendChild(box);if(g)showLog({agent:g.agent},box);else box.textContent='No agent on this role yet.'}
+ })})}
window.d2PipeMap=function(host,d){
host.innerHTML='';var complex=d.view==='complex',ag=d.agents||{},you=d.you||0;
- var roles=complex?['designer','coder'].concat(ag.tester==='working'?['tester']:[]).concat(['guardian']):[];
- var W=complex?90+roles.length*95:300,Y=52,r=complex?21:24;
- var svg=el('svg',{viewBox:'0 0 '+W+' 120',role:'img'});
+ var agentsMode=d.mode==='agents',list=d.list||{};
+ if(agentsMode){ag={};Object.keys(list).forEach(function(k){ag[k]=list[k].map(function(x){return x.state})});d.ctx=d.ctx||{};Object.keys(list).forEach(function(k){var c=list[k].map(function(x){return x.ctx}).filter(function(v){return v!=null});if(c.length)d.ctx[k]=c})}
+ var roles=agentsMode&&d.roles?d.roles.slice():complex?['designer','coder'].concat(ag.tester==='working'?['tester']:[]).concat(['guardian']):[];
+ var W=complex?90+roles.length*95:300,Y=52,r=complex?21:24,look=function(svg,x,y){if(!d.look)return;var a=el('a',{href:'?look=1'});
+ a.appendChild(el('text',{x:x,y:y,'text-anchor':'middle','font-size':11,fill:'var(--attn,#7a7fc9)'},d.look+' to look at'));svg.appendChild(a)};
+ var svg=el('svg',{viewBox:'0 0 '+W+' '+(d.look?130:120),role:'img'});
svg.appendChild(el('title',{},'How work flows: you, '+(complex?roles.join(', '):'the pipeline, your AI')));
var defs=el('defs',{}),m=el('marker',{id:'pm-a',viewBox:'0 0 10 10',refX:8,refY:5,markerWidth:6,markerHeight:6,orient:'auto'});
…
var youSub=you?you+' for you':'nothing for you';
if(!complex){
- node(svg,38,Y,r,'you',youSub,null,'?who=mine',true,'you');
+ node(svg,38,Y,r,'you',youSub,null,'?who=mine',true,'you');look(svg,38,Y+r+50);
arrow(svg,64,100,Y);
var box=el('g',{});box.appendChild(el('rect',{x:102,y:Y-22,width:96,height:44,rx:10,fill:'none',stroke:'var(--soft,#9c9a93)','stroke-dasharray':'4 3'}));
…
pills(box,150,Y+1,d.items||{});svg.appendChild(box);
arrow(svg,200,236,Y);
- var st=ag.maker||null;node(svg,262,Y,r,'AI',st||'not started',st,'?role=maker',false,'AI');
+ var RM=roll(ag.maker),st=RM?RM.top:null;node(svg,262,Y,r,'AI',RM?RM.sub:'not started',st,'?role=maker',false,'AI',RM);
} else {
- node(svg,32,Y,r,'you',youSub,null,'?who=mine',true,'you');
- var x=32;roles.forEach(function(role){var nx=x+95;arrow(svg,x+r+2,nx-r,Y);
- pills(svg,(x+nx)/2,Y-38,(d.items||{})[role]||{});
- var s=ag[role]||null;node(svg,nx,Y,r,role,s||'not started',s,'?role='+encodeURIComponent(role),false,role.charAt(0).toUpperCase());x=nx;});
+ node(svg,32,Y,r,'you',youSub,null,'?who=mine',true,'you');look(svg,32,Y+r+50);
+ var KNOWN=['designer','coder','tester','guardian'],sep=false;
+ var x=32;roles.forEach(function(role){var nx=x+95,cxs=d.ctx||{},known=KNOWN.indexOf(role)>=0;
+ if(!known&&!sep){sep=true;svg.appendChild(el('path',{d:'M'+(x+47)+' '+(Y-26)+'V'+(Y+26),stroke:'var(--line,#2c3039)','stroke-width':1.5,'stroke-dasharray':'2 3'}))}
+ if(known)arrow(svg,x+r+2+(cxs[x===32?'':roles[roles.indexOf(role)-1]]!=null?7:0),nx-r-(cxs[role]!=null?8:0),Y);
+ if(known)pills(svg,(x+nx)/2,Y-38,(d.items||{})[role]||{});
+ var R=roll(ag[role]),s=R?R.top:null,cx=(d.ctx||{})[role];if(Array.isArray(cx))cx=cx.length?Math.max.apply(null,cx):null;
+ if(s&&cx!=null){var RR=r+6,C=2*Math.PI*RR,pct=Math.max(0,Math.min(100,cx));
+ svg.appendChild(el('circle',{cx:nx,cy:Y,r:RR,fill:'none',stroke:'var(--line,#2c3039)','stroke-width':4}));
+ var ring=el('circle',{cx:nx,cy:Y,r:RR,fill:'none',stroke:pct>=70?'#e07a2e':'#5a8fc7','stroke-width':4,'stroke-linecap':'round','stroke-dasharray':(C*pct/100).toFixed(1)+' '+C.toFixed(1),transform:'rotate(-90 '+nx+' '+Y+')'});
+ ring.appendChild(el('title',{},role+': context ~'+Math.round(pct)+'%'));svg.appendChild(ring);}
+ node(svg,nx,Y,r,role,(R?R.sub:'not started')+(s&&cx!=null&&R.n===1?' · '+Math.round(cx)+'%':''),s,(agentsMode?'#r='+encodeURIComponent(role):'?role='+encodeURIComponent(role)),false,role.charAt(0).toUpperCase(),R);x=nx;});
}
+ if(complex&&d.waits&&d.waits.length){var X={you:32};var xx=32;roles.forEach(function(ro){xx+=95;X[ro]=xx});
+ var dm=el('marker',{id:'pm-w',viewBox:'0 0 10 10',refX:8,refY:5,markerWidth:6,markerHeight:6,orient:'auto'});dm.appendChild(el('path',{d:'M0 0L10 5L0 10z',fill:'#d9a520'}));defs.appendChild(dm);
+ d.waits.forEach(function(w,i){var a=X[w.from],b=X[w.to];if(a==null||b==null||a===b)return;var o=d.look?10:0,yb=Y+r+44+o+i*10,dip=yb+14;
+ var p=el('path',{d:'M'+a+' '+(Y+r+40+o)+' C'+a+' '+dip+' '+b+' '+dip+' '+b+' '+(Y+r+40+o),stroke:'#d9a520','stroke-width':1.5,'stroke-dasharray':'4 3',fill:'none','marker-end':'url(#pm-w)'});
+ p.appendChild(el('title',{},w.from+' is waiting on '+w.to));svg.appendChild(p);});
+ svg.setAttribute('viewBox','0 0 '+W+' '+(140+d.waits.length*10+(d.look?10:0)));}
host.appendChild(svg);
+ if(agentsMode)details(host,svg,d,list);
if(d.help){var h=document.createElement('a');h.className='pm-help';h.href=d.help;h.textContent='How it works ›';host.appendChild(h);}
};
…
<div class="pm-map" style="max-width:380px;margin:0 auto"></div>
<script>
-d2PipeMap(document.querySelector(".pm-map"), {view:"complex",you:1,items:{designer:{"new":2,"in-progress":1},coder:{"new":3,"in-progress":1,waiting:1},tester:{},guardian:{"new":1}},agents:{designer:"working",coder:"waiting",tester:null,guardian:"idle"}});
+d2PipeMap(document.querySelector(".pm-map"), {view:"complex",you:1,items:{designer:{"new":2,"in-progress":1},coder:{"new":3,"in-progress":1,waiting:1},tester:{},guardian:{"new":1}},agents:{designer:"working",coder:["working","waiting"],tester:null,guardian:"idle"}});
document.querySelector(".pm-map").addEventListener("click", function (e) { if (e.target.closest("a")) e.preventDefault() }); // a sample: links are inert here
</script>
…
**Tips.**
- Start a chat by naming its role and "work the pipeline".
-- "Park it" makes an item instead of doing the work now.
+- "Park it" makes an item and parks it, instead of doing the work now.
- Ask for links, so you can open each item.
-- Say "mark it important" (or "drop it", "move it to the to-do list") and your AI does it for you, recorded as asked by you ([[Spec#^pipe-37]]).
+- Say "mark it important" (or "drop it", "demote it to a to-do") and your AI does it for you, recorded as asked by you ([[Spec#^pipe-37]]).
- New work for the coder waits for your review unless your Permissions say otherwise.
## Parking an idea
-Say *"park it"* to your AI about an idea and it becomes an item for you, with your words at the top of its document. You can also make one from the Pipeline page (**New item**). An item has a title, who it's from and who it's for (a role, and a person), a kind (question, design, code, test, handover…) and a document that grows as people and agents work on it.
+Say *"park it"* to your AI about an idea and it becomes an item for you, parked (never picked until you unpark it), with your words at the top of its document. You can also make one from the Pipeline page (**New item**). An item has a title, who it's from and who it's for (a role, and a person), a kind (question, design, code, test, handover…) and a document that grows as people and agents work on it.
## From → for, and who took it
…
- **Promote** a to-do (also **To pipeline** in the Backlog): it becomes an item with its text and feature; the to-do stays, ticked, with a link to the item, as your draft of that topic.
-- **Move to to-do** an item that should wait: it becomes a to-do in a topic you pick (by default `ToDo:PROJECT`, under *From the pipeline*, made if missing; *Parked: needs more thinking* is for to-dos you park by hand), as your draft, and the item is closed as **moved to to-do**, linked to its to-do. (*Parking* is the other way round: an idea becomes an item for later.)
-- Promoting and moving to to-do are your calls. Your AI does them **when you ask it to** (it names you as the one who asked, and the item's history says so); otherwise it **suggests**: an item for you, kind `suggestion`, listing what to promote, move to to-do or drop, each linked, and you decide on the Work page.
+- **Demote to a to-do** an item that should wait: it becomes a to-do in a topic you pick (by default `ToDo:PROJECT`, under *From the pipeline*, made if missing; *Parked: needs more thinking* is for to-dos you park by hand), as your draft, and the item is closed as a to-do, linked to it. (*Parking* keeps an item in the pipeline, on hold, until you unpark it.)
+- Promoting and demoting are your calls. Your AI does them **when you ask it to** (it names you as the one who asked, and the item's history says so); otherwise it **suggests**: an item for you, kind `suggestion`, listing what to promote, demote or drop, each linked, and you decide on the Work page.
## Only people move work between people
…