great-cto 3.76.1 → 3.78.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.
@@ -1,13 +1,13 @@
1
1
  /* Task-first projection. Existing controller commands and authorization remain authoritative. */
2
2
  (() => {
3
3
  const esc = s => String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
4
- const aliases = { docs:'knowledge', usage:'insights/consumption', ledger:'insights/ledger', budgets:'insights/ledger', dashboard:'insights/ledger', fleet:'settings/executors', agents:'settings/executors', harness:'settings/runtime', skills:'settings/capabilities', history:'work/completed', inbox:'decisions', notifications:'settings/general', share:'settings/general' };
4
+ const aliases = { docs:'knowledge', usage:'insights/consumption', ledger:'insights/ledger', budgets:'insights/ledger', dashboard:'insights/ledger', fleet:'settings/executors', agents:'settings/executors', harness:'settings/runtime', skills:'settings/capabilities', history:'work/completed', inbox:'home', decisions:'home', cost:'insights/ledger', insights:'insights/ledger', notifications:'settings/general', share:'settings/general' };
5
5
  function route(input) {
6
6
  const taskRoute = window.GctoWork?.parseRoute?.(input);
7
7
  if (taskRoute) return { canonical:taskRoute.canonical, panel:'work', primary:'work', workRoute:true };
8
8
  if (/^(?:agents|fleet)\/[a-z0-9-]+$/i.test(input)) return {canonical:input,panel:'agents',primary:'settings'};
9
9
  const canonical = aliases[input] || input;
10
- const panels = { work:'work','work/completed':'history',decisions:'inbox',knowledge:'knowledge',insights:'admin-outcomes','insights/outcomes':'admin-outcomes','insights/consumption':'usage','insights/ledger':'budgets',settings:'agents','settings/executors':'agents','settings/runtime':'harness','settings/capabilities':'skills','settings/general':'settings' };
10
+ const panels = { home:'inbox', work:'work','work/completed':'history',knowledge:'knowledge',insights:'admin-outcomes','insights/outcomes':'admin-outcomes','insights/consumption':'usage','insights/ledger':'budgets',settings:'agents','settings/executors':'agents','settings/runtime':'harness','settings/capabilities':'skills','settings/general':'settings' };
11
11
  if (!panels[canonical]) return null;
12
12
  return { canonical, panel:panels[canonical], primary:canonical.split('/')[0] };
13
13
  }
@@ -16,7 +16,7 @@
16
16
  const completed = tasks.filter(e => e.taskOutcome?.state === 'completed').length;
17
17
  return {tasks:tasks.length,completed,unknown:tasks.length-completed,acceptanceRate:null,costPerAcceptedTask:null};
18
18
  }
19
- let generation=0, selectedDoc=null, selectedRoute='work', request=0, indexRequest=0;
19
+ let generation=0, selectedDoc=null, selectedRoute='home', request=0, indexRequest=0;
20
20
  const scope = () => new URLSearchParams(location.search).get('project') || '';
21
21
  const scoped = path => path+(path.includes('?')?'&':'?')+'project='+encodeURIComponent(scope());
22
22
  const panel = id => document.getElementById(id);
@@ -38,7 +38,7 @@
38
38
  el.innerHTML=`<p class="work-meta">Observed ${esc(data.observedAt)} · ${data.health==='current'?'Sources read':'Sources incomplete'} · Recorded task metadata, not a benchmark</p>
39
39
  <div class="admin-metrics"><section><h2>${d.tasks}</h2><p>Tracked tasks</p><small>Legacy issues and standalone runs excluded</small></section><section><h2>${d.completed}</h2><p>Explicitly completed outcomes</p><small>Recorded completion, not an acceptance-rate estimate</small></section><section><h2>${d.unknown}</h2><p>No completed outcome recorded</p><small>Includes in-progress tasks; not a failure count</small></section><section><h2>Unavailable</h2><p>Cost per accepted task</p><small>Complete task-bound actual spend is not available</small></section></div>
40
40
  <div class="admin-notice">Acceptance rate, time to acceptance and severe regressions are not measured by this projection. Role verdicts, closed issues and successful processes cannot supply those numbers.</div>
41
- <h2 class="admin-section-title">Recorded work</h2><div class="admin-table-scroll"><table class="admin-table"><thead><tr><th>Task</th><th>Host</th><th>State</th><th>Outcome</th></tr></thead><tbody>${(data.entries||[]).filter(e=>e.kind==='task').slice(0,100).map(e=>`<tr><td>${esc(e.title)}</td><td>${esc(e.host||'Not linked')}</td><td>${esc(e.phase||'Unknown')}</td><td>${esc(e.taskOutcome?.state||'Not recorded')}</td></tr>`).join('')||'<tr><td colspan="4">No tracked task outcomes in the readable records.</td></tr>'}</tbody></table></div><p class="work-meta">All available tracked tasks, not a time-window cohort. Host quality comparisons require matched tasks, budgets and acceptance criteria.</p>`;
41
+ <h2 class="admin-section-title" id="admin-outcomes-work">Recorded work</h2><div class="admin-table-scroll" tabindex="0" role="region" aria-labelledby="admin-outcomes-work"><table class="admin-table"><thead><tr><th>Task</th><th>Host</th><th>State</th><th>Outcome</th></tr></thead><tbody>${(data.entries||[]).filter(e=>e.kind==='task').slice(0,100).map(e=>`<tr><td>${esc(e.title)}</td><td>${esc(e.host||'Not linked')}</td><td>${esc(e.phase||'Unknown')}</td><td>${esc(e.taskOutcome?.state||'Not recorded')}</td></tr>`).join('')||'<tr><td colspan="4">No tracked task outcomes in the readable records.</td></tr>'}</tbody></table></div><p class="work-meta">All available tracked tasks, not a time-window cohort. Host quality comparisons require matched tasks, budgets and acceptance criteria.</p>`;
42
42
  } catch(error) { if(current(seq,project)&&ticket===request) el.textContent='Outcomes could not be read: '+error.message; }
43
43
  }
44
44
  async function loadKnowledge() {
@@ -102,7 +102,14 @@
102
102
  function setup() {
103
103
  const nav=panel('nav-tablist'), existing={};
104
104
  document.querySelectorAll('.nav-item[data-tab]').forEach(n=>existing[n.dataset.tab]=n.querySelector('svg')?.outerHTML||'');
105
- nav.innerHTML=['work','decisions','knowledge','insights','settings'].map(key=>`<button type="button" class="nav-item${key==='work'?' active':''}" role="tab" aria-selected="${key==='work'}" data-tab="${key}" aria-label="${key[0].toUpperCase()+key.slice(1)}" data-nav-label="${key[0].toUpperCase()+key.slice(1)}" tabindex="0" onkeydown="navTabKeydown(event,this)">${existing[key]||existing[key==='knowledge'?'skills':key==='insights'?'usage':'harness']||''}<span class="nav-label">${key[0].toUpperCase()+key.slice(1)}</span>${key==='decisions'?'<span class="count" id="nav-inbox-count">0</span>':''}</button>`).join('');
105
+ // Home · Work · Cost · Knowledge · Settings (UX audit 2026-11, approved mockup).
106
+ // Route keys are unchanged where a screen is unchanged: Cost is still the
107
+ // `insights` route, Home is the panel Decisions used; `#/decisions` lands on Home.
108
+ const NAV_LABEL={home:'Home',work:'Work',insights:'Cost',knowledge:'Knowledge',settings:'Settings'};
109
+ // Cost is one screen (F4): `insights` opens it; Token detail (`insights/consumption`)
110
+ // and Outcomes (`insights/outcomes`, hidden until it has data) stay reachable by route.
111
+ const NAV_ICON={home:'decisions',work:'work',insights:'ledger',knowledge:'skills',settings:'harness'};
112
+ nav.innerHTML=['home','work','insights','knowledge','settings'].map(key=>`<button type="button" class="nav-item${key==='home'?' active':''}" role="tab" aria-selected="${key==='home'}" data-tab="${key}" aria-label="${NAV_LABEL[key]}" data-nav-label="${NAV_LABEL[key]}" tabindex="0" onkeydown="navTabKeydown(event,this)">${existing[NAV_ICON[key]]||''}<span class="nav-label">${NAV_LABEL[key]}</span>${key==='home'?'<span class="count" id="nav-inbox-count" title="items that need you in this project">0</span>':''}</button>`).join('');
106
113
  nav.querySelectorAll('[data-tab]').forEach(n=>n.addEventListener('click',()=>window.switchTab(n.dataset.tab)));
107
114
  const tools=panel('tools-nav');tools.hidden=true;
108
115
  const workspace=document.querySelector('.workspace');
@@ -137,8 +144,8 @@
137
144
  if (r.workRoute && typeof currentTab === 'string' && currentTab === 'work') { closeMenu(false); window.GctoWork.refresh(); }
138
145
  else original(old); // Keep established loaders and mutation guards; task inspection adds no view POST.
139
146
  document.querySelectorAll('.nav-item[data-tab]').forEach(n=>{const active=n.closest('#nav-tablist')&&n.dataset.tab===r.primary;n.classList.toggle('active',!!active);n.setAttribute('aria-selected',String(!!active));});
140
- panel('crumb-here').textContent=r.primary[0].toUpperCase()+r.primary.slice(1);
141
- const items=r.primary==='settings'?[['settings/executors','Executors'],['settings/runtime','Runtime'],['settings/capabilities','Capabilities'],['settings/general','General']]:r.primary==='insights'?[['insights/outcomes','Outcomes'],['insights/consumption','Consumption'],['insights/ledger','Ledger & budgets']]:[];
147
+ panel('crumb-here').textContent=({home:'Home',insights:'Cost'})[r.primary]||r.primary[0].toUpperCase()+r.primary.slice(1);
148
+ const items=r.primary==='settings'?[['settings/executors','Executors'],['settings/runtime','Runtime'],['settings/capabilities','Capabilities'],['settings/general','General']]:[];
142
149
  bar.hidden=!items.length;bar.innerHTML=items.map(([key,label])=>`<button type="button" class="admin-subtab" data-route="${key}" aria-pressed="${r.canonical===key||(r.canonical===r.primary&&key===items[0][0])}">${label}</button>`).join('');
143
150
  bar.querySelectorAll('[data-route]').forEach(b=>b.onclick=()=>window.switchTab(b.dataset.route));
144
151
  history.replaceState(history.state,'','#/'+r.canonical);
@@ -149,7 +156,7 @@
149
156
  const composer=panel('work-compose');
150
157
  const disclosure=document.createElement('details');disclosure.className='admin-compose';const summary=document.createElement('summary');summary.textContent='New task · prepare launch command';disclosure.append(summary);composer.before(disclosure);disclosure.append(composer);
151
158
  composer.querySelector('button[type="submit"]').textContent='Copy launch command';
152
- window.switchTab((location.hash||'#/work').replace(/^#\/?/,''));
159
+ window.switchTab((location.hash||'#/home').replace(/^#\/?/,''));
153
160
  }
154
161
  window.GctoAdmin={route,outcomes,reset,openDocument,setWorkView,navigateWork};
155
162
  if(typeof document!=='undefined') setup();
@@ -20,7 +20,7 @@
20
20
  [/architect|adr|design|prompt|decision-scorer|project-auditor/, 'arch'],
21
21
  [/security|sec-|threat|pci|gdpr|hipaa|smart-contract/, 'security'],
22
22
  [/\bqa\b|test|eval|performance|^code-reviewer$/, 'qa'],
23
- [/devops|deploy|infra|l3|support|oncall/, 'ops'],
23
+ [/devops|deploy|infra|l3|support|oncall|release/, 'ops'],
24
24
  [/reviewer$/, 'domain'],
25
25
  [/pm|plan|product|coordinator/, 'pm'],
26
26
  [/learn|memory|continuous|knowledge/, 'memory'],