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.
- package/board/.claude-plugin/plugin.json +3 -2
- package/board/agents/devops.md +19 -0
- package/board/agents/e2e-test-engineer.md +20 -1
- package/board/agents/release-manager.md +213 -0
- package/board/agents/security-officer.md +53 -14
- package/board/packages/board/lib/config.mjs +27 -6
- package/board/packages/board/lib/installed-version.mjs +52 -0
- package/board/packages/board/lib/routes.mjs +5 -12
- package/board/packages/board/lib/util.mjs +19 -1
- package/board/packages/board/public/assets/admin-shell.js +15 -8
- package/board/packages/board/public/assets/agent-groups.js +1 -1
- package/board/packages/board/public/index.html +690 -364
- package/board/packages/board/server.mjs +29 -8
- package/board/scripts/codex-pipeline.mjs +57 -12
- package/board/scripts/lib/approval-level.mjs +9 -2
- package/board/scripts/lib/codex-pipeline.mjs +114 -29
- package/board/scripts/lib/codex-role-profiles.mjs +1 -0
- package/board/scripts/lib/exceptions.mjs +167 -0
- package/board/scripts/lib/gate-check-bound.mjs +478 -0
- package/board/scripts/lib/gate-check.mjs +173 -0
- package/board/scripts/lib/gate-reversibility.mjs +4 -0
- package/board/scripts/lib/gate-tier.mjs +1 -1
- package/board/scripts/lib/ship-evidence-context.mjs +174 -0
- package/board/scripts/lib/ship-evidence-identity.mjs +294 -0
- package/board/scripts/lib/work-tasks.mjs +16 -2
- package/board/shared/pipeline.toml +41 -0
- package/dist/main.js +5 -3
- package/package.json +1 -1
|
@@ -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 => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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',
|
|
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='
|
|
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
|
-
|
|
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']]:
|
|
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||'#/
|
|
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'],
|