docs: [verified] connect Strategy through practical Delivery journey
Build and publish Eldorado documentation / build (pull_request) Successful in 42s

This commit is contained in:
jarvis-at-skic committed 2026-10-08 13:29:59 +00:00
1 parent c934ddb5e0
commit f7a68d7e37
50 files changed
+1573 -95

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
const {chromium}=require('playwright');
const fs=require('node:fs');const assert=require('node:assert/strict');
(async()=>{
const base=process.argv[2]||'http://127.0.0.1:4173';const out=`.cache/journey-${process.argv[3]||'candidate'}`;fs.mkdirSync(out,{recursive:true});
const b=await chromium.launch({executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']});const results=[];
const steps=[['target-operating-model','roadmap'],['roadmap','stage-01-skills-adoption'],['stage-01-skills-adoption','baseline'],['baseline','measurement'],['measurement','skills-quality'],['skills-quality','skills-governance'],['skills-governance','skills-quality'],['skills-quality','skills-adoption'],['skills-adoption','skills-maintenance'],['skills-maintenance','skills-quality'],['skills-quality','measurement/validated-task-success'],['measurement/validated-task-success','skills-quality'],['skills-maintenance','stage-02-autonomous-agent'],['stage-02-autonomous-agent','stage-01-skills-adoption']];
try{for(const width of [1440,390]){const p=await b.newPage({viewport:{width,height:900}});for(const [from,to]of steps){await p.goto(`${base}/eldorado-v1/documentation/strategy/${from}/`,{waitUntil:'networkidle'});const href=`/eldorado-v1/documentation/strategy/${to}/`;const link=p.locator(`article a[href="${href}"]`).filter({visible:true}).first();assert(await link.count(),`${from} → ${to}`);await link.scrollIntoViewIfNeeded();await p.waitForLoadState('networkidle');await link.click();await p.waitForURL(`${base}${href}`);await p.locator('article h1').waitFor();results.push({width,from,to,clicked:true});fs.writeFileSync(`${out}/results.json`,JSON.stringify(results,null,2))}await p.close()}console.log(`PASS ${results.length} real reader handoffs and feedback links`)}finally{await b.close()}
})().catch(e=>{console.error(e);process.exitCode=1});
+1 -1
View File
@@ -125,7 +125,7 @@ const fs = require('node:fs');
await page.screenshot({path:`${output}/${width}.png`, fullPage:true});
await frame.locator('#btn-theme').click(); await theme('dark');
// Exercise keyboard navigation without racing the portal's smooth scroll.
await page.locator('.strategy-content a[href$="/strategy/roadmap/"]').filter({visible:true}).focus();
await page.locator('.strategy-content a[href$="/strategy/roadmap/"]').filter({visible:true}).first().focus();
await page.keyboard.press('Enter');
await page.waitForURL('**/strategy/roadmap/');
await page.goBack({waitUntil:'domcontentloaded'});
+8 -2
View File
@@ -7,7 +7,7 @@ const assert=require('node:assert/strict');
const browser=await chromium.launch({executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']});
const results=[];
try {
for(const topic of ['measurement','baseline','governance','quality','adoption','maintenance']) for(const width of [1440,1024,390,320]){
for(const topic of ['measurement','baseline','governance','quality','adoption','maintenance','stage01','stage02']) for(const width of [1440,1024,390,320]){
const spec=JSON.parse(fs.readFileSync(`diagrams/strategy-${topic}${width<=1100?'-mobile':''}.architecture.json`));
const page=await browser.newPage({viewport:{width:1200,height:1000}});
await page.goto('file://'+path.resolve(`${dir}/${topic}-${width}.svg`));
@@ -20,17 +20,23 @@ const assert=require('node:assert/strict');
// text presence alone cannot detect one label hiding another.
const labels=[...svg.querySelectorAll('g[data-edge-id]')].filter(el=>el.querySelector('text')).map(el=>({id:el.dataset.edgeId,box:el.getBoundingClientRect()}));
const labelOverlaps=[];
const nodes=[...svg.querySelectorAll('[data-node-id]')].map(el=>({id:el.dataset.nodeId,box:el.getBoundingClientRect()}));
const labelNodeOverlaps=[];
for(const a of labels)for(const b of nodes){
if(Math.min(a.box.right,b.box.right)>Math.max(a.box.left,b.box.left)&&Math.min(a.box.bottom,b.box.bottom)>Math.max(a.box.top,b.box.top))labelNodeOverlaps.push([a.id,b.id]);
}
for(let i=0;i<labels.length;i++)for(let j=i+1;j<labels.length;j++){
const a=labels[i],b=labels[j];
if(Math.min(a.box.right,b.box.right)>Math.max(a.box.left,b.box.left)&&Math.min(a.box.bottom,b.box.bottom)>Math.max(a.box.top,b.box.top))labelOverlaps.push([a.id,b.id]);
}
return {labelOverlaps,viewBox:[v.width,v.height],nodes:[...svg.querySelectorAll('[data-node-id]')].map(n=>n.dataset.nodeId),edges:[...svg.querySelectorAll('path[data-edge-id]')].map(n=>n.dataset.edgeId),text:svg.textContent,issues};
return {labelOverlaps,labelNodeOverlaps,viewBox:[v.width,v.height],nodes:[...svg.querySelectorAll('[data-node-id]')].map(n=>n.dataset.nodeId),edges:[...svg.querySelectorAll('path[data-edge-id]')].map(n=>n.dataset.edgeId),text:svg.textContent,issues};
});
assert.deepEqual(measured.viewBox,spec.meta.viewBox);
assert.deepEqual(measured.nodes.sort(),spec.components.map(n=>n.id).sort());
assert.deepEqual(measured.edges.sort(),spec.connections.map(e=>e.id).sort());
assert.deepEqual(measured.issues,[]);
assert.deepEqual(measured.labelOverlaps,[],`${topic}-${width}: rendered edge labels overlap`);
assert.deepEqual(measured.labelNodeOverlaps,[],`${topic}-${width}: edge label obscures a node`);
for(const n of spec.components){assert(measured.text.includes(n.label));assert(measured.text.includes(n.tag));for(const line of n.sublabel.split('\n'))assert(measured.text.includes(line));}
for(const e of spec.connections)if(e.label)assert(measured.text.includes(e.label));
if(width===1440){
+3 -1
View File
@@ -17,7 +17,9 @@ const fs = require('node:fs');
assert.equal(response.status(), 200);
assert.equal(await page.locator('h1').innerText(), 'Skills Measurement');
assert((await page.title()).startsWith('Skills Measurement |'));
assert.equal(await page.locator('.strategy-content details').count(), 0);
assert.equal(await page.locator('.strategy-content details').count(), 1);
assert.equal(await page.locator('.strategy-content details[open]').count(), 0);
assert((await page.locator('.strategy-content details summary').innerText()).includes('Implementation status'));
assert.equal(await page.locator('.strategy-content h2').count(), 2);
assert.deepEqual((await page.locator('.strategy-content h3').allTextContents()).map(text => text.replaceAll('\u200b', '')), ['Workflow effectiveness', 'Skills effectiveness & health']);
assert.equal(await page.locator('.strategy-content ol').count(), 2);
+2 -2
View File
@@ -6,10 +6,10 @@ const assert=require('node:assert/strict');
const out=`.cache/skills-${process.argv[3]||'candidate'}`;fs.mkdirSync(out,{recursive:true});
const browser=await chromium.launch({executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']}); const results=[];
try {
for(const topic of ['measurement','baseline','governance','quality','adoption','maintenance']) for(const width of [1440,1024,390,320]) {
for(const topic of ['measurement','baseline','governance','quality','adoption','maintenance','stage01','stage02']) for(const width of [1440,1024,390,320]) {
console.log(`Checking ${topic} at ${width}px`);
const page=await browser.newPage({viewport:{width,height:900},colorScheme:'light'});const errors=[];page.on('pageerror',e=>errors.push(e.message));
const route=['measurement','baseline'].includes(topic)?topic:`skills-${topic}`;
const route=topic==='stage01'?'stage-01-skills-adoption':topic==='stage02'?'stage-02-autonomous-agent':['measurement','baseline'].includes(topic)?topic:`skills-${topic}`;
assert.equal((await page.goto(`${base}/eldorado-v1/documentation/strategy/${route}/`,{waitUntil:'networkidle'})).status(),200);
const panel=page.locator('.archify-panel--skills');assert.equal(await panel.count(),1);
const iframe=panel.locator(width>1100?'.story-desktop':'.story-mobile');await iframe.scrollIntoViewIfNeeded();
+10
View File
@@ -81,6 +81,16 @@ const fs = require('node:fs');
await closeMenu();
}
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), `${width}: overflow on ${id}`);
if(width===1440 || width===390) {
// Full-page capture does not trigger below-fold lazy iframes.
for(const iframe of await page.locator('.archify-panel--story > iframe').filter({visible:true}).all()) {
await iframe.scrollIntoViewIfNeeded();
const frame=await (await iframe.elementHandle()).contentFrame();
await frame.locator('.diagram-container > svg').waitFor();
}
await page.evaluate(()=>scrollTo({top:0,behavior:'instant'}));
await page.screenshot({path:`${output}/${width}-${id.replaceAll('/','-')}.png`,fullPage:true,animations:'disabled'});
}
}
assert((await page.locator('article').innerText()).includes('not deployed autonomy'));
await page.screenshot({path:`${output}/${width}-stage-02.png`, fullPage:true, animations:'disabled'});
+2 -2
View File
@@ -24,7 +24,7 @@ const fs=require('node:fs');
assert.equal(geometry.nodes.length,(source.components||source.nodes).length,id);
for(const n of geometry.nodes) assert(n.x>=0 && n.y>=0 && n.right<=geometry.width+1 && n.bottom<=geometry.height+1,`${id} ${width} clipped ${JSON.stringify(n)} / ${geometry.height}`);
const data=await page.locator('.strategy-content').evaluate(el=>({words:el.innerText.trim().split(/\s+/).length,overflow:document.documentElement.scrollWidth>innerWidth,openDetails:el.querySelectorAll('details[open]').length}));
assert(data.words<=(id==='measurement'?1500:id==='roadmap'?600:200),id);assert(!data.overflow,id);assert.equal(data.openDetails,0,id);
assert(data.words<=(id==='measurement'?1600:id==='roadmap'?600:550),id);assert(!data.overflow,id);assert.equal(data.openDetails,0,id);
await page.evaluate(()=>scrollTo({top:0,behavior:'instant'}));await page.screenshot({path:`.cache/story-${label}/${width}-${id}.png`,fullPage:true});
const opener=panel.getByRole('button',{name:/^Expand /});await opener.click();
const close=panel.getByRole('button',{name:'Close full-screen diagram',exact:true});await close.waitFor({state:'visible'});
@@ -35,7 +35,7 @@ const fs=require('node:fs');
// Native close clears open before the queued React onClose restores focus.
// Wait for its unmount before focusing the next keyboard interaction target.
await panel.locator('dialog iframe').waitFor({state:'detached'});assert(await opener.evaluate(n=>n===document.activeElement));
if(id !== 'measurement') {
if(await page.locator('.strategy-content > details > summary').count()) {
const summary=page.locator('.strategy-content > details > summary').first();
await summary.focus();await page.keyboard.press('Enter');
await page.waitForFunction(()=>document.querySelector('.strategy-content > details').open);
+58
View File
@@ -0,0 +1,58 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync,readdirSync} from 'node:fs';
import {createHash} from 'node:crypto';
const read=p=>readFileSync(p,'utf8');
const coverage=JSON.parse(read('research/ai-adoption/strategy-coverage.json')).routes;
test('connected Strategy covers every canonical route and reader handoff',()=>{
const files=readdirSync('docs/strategy',{recursive:true}).filter(p=>p.endsWith('.mdx')).map(p=>'docs/strategy/'+p).sort();
assert.equal(coverage.length,28);
assert.deepEqual(coverage.map(r=>r.source).sort(),files);
assert.equal(new Set(coverage.map(r=>r.route)).size,28);
for(const r of coverage){
for(const k of ['question','inputs','action_or_decision','output','next_or_feedback','review'])assert(r[k],`${r.source}: ${k}`);
const page=read(r.source);assert(!/roadmap gates|Evidence-gated phases/.test(page),r.source);
assert(coverage.some(n=>n.route===r.next_or_feedback),r.next_or_feedback);
if(r.contract_sha256){
const body=page.split('{/* contract:start */}\n')[1].split('\n{/* contract:end */}')[0];
assert.equal(createHash('sha256').update(body).digest('hex'),r.contract_sha256,r.source);
}
}
assert.equal(coverage.filter(r=>r.implementation==='prototype').length,9);
assert.equal(coverage.filter(r=>r.implementation==='unimplemented').length,7);
});
test('the parent journey connects qualification, pilot authority and feedback without automatic autonomy',()=>{
const parent=read('docs/strategy/stage-01-skills-adoption.mdx');
for(const marker of ['illustrative Delivery','independent Quality Engineering reviewer','baseline describes work; it grants no approval','not six rigid sequential approval gates','Governance bounds every activity','separate case for authority and readiness'])assert(parent.toLowerCase().includes(marker.toLowerCase()),marker);
const governance=read('docs/strategy/skills-governance.mdx');
for(const marker of ['experimental → bounded pilot','bounded pilot → approved','Initial experimental qualification runs only in approved disposable environments','Real work requires a separate bounded-pilot authorization'])assert(governance.includes(marker),marker);
assert(!governance.includes('| experimental → approved |'));
assert(read('docs/strategy/skills-adoption.mdx').includes('pilot decision alone is not general approval'));
const quality=read('docs/strategy/skills-quality.mdx');
for(const marker of ['Qualification artifact success is not live task success','planned executed trials','matured real-workflow starts','regression against the currently approved revision'])assert(quality.includes(marker),marker);
for(const id of ['measurement/index','skills-maintenance'])assert(read(`docs/strategy/${id}.mdx`).includes('skills-quality.mdx'));
const future=read('docs/strategy/stage-02-autonomous-agent.mdx');
for(const marker of ['separate authority decision','Unresolved release approval roles remain a hold condition','not deployed autonomy','stop/human-takeover','recorded hold'])assert(future.includes(marker),marker);
});
for(const stage of ['stage01','stage02'])test(`${stage}: native paired views preserve handoffs, decisions and containment`,()=>{
const pair=['','-mobile'].map(s=>JSON.parse(read(`diagrams/strategy-${stage}${s}.architecture.json`)));
const nodes=d=>d.components.map(({pos,size,...n})=>n);
const relationships=d=>d.connections.map(({id,from,to,variant})=>({id,from,to,variant}));
assert.deepEqual(nodes(pair[0]),nodes(pair[1]));
assert.deepEqual(relationships(pair[0]),relationships(pair[1]));
const expected=stage==='stage01'?[['baseline','engineer'],['engineer','qualify'],['qualify','pilot'],['pilot','decide'],['decide','maintain'],['maintain','engineer'],['decide','baseline']]:[['proven','case'],['case','decision'],['decision','execute'],['decision','hold'],['execute','stop'],['stop','hold']];
assert.deepEqual(pair[0].connections.map(e=>[e.from,e.to]).sort(),expected.sort());
const by=Object.fromEntries(pair[0].components.map(n=>[n.id,n]));
if(stage==='stage01'){
assert(by.qualify.sublabel.includes('Fail / incomplete: hold or rework'));
assert(by.reference.tag.includes('Throughout'));
assert(by.decide.sublabel.includes('Compare with baseline, not trials'));
}else{
assert(by.decision.sublabel.includes('Owning approver'));
assert(by.stop.sublabel.includes('Revoke mutations'));
assert(by.hold.sublabel.includes('No automatic progression'));
}
});
+4 -1
View File
@@ -36,7 +36,10 @@ test('each original metric survives exactly once on its own canonical Measuremen
const page = read(`docs/strategy/measurement/${part.slug}.mdx`);
const body = page.split('{/* preserved:start */}\n')[1]?.split('\n{/* preserved:end */}')[0];
// Only the cross-page navigation wording changes; hash all original substance.
const original = body.replace('Use the [metric definitions](./index.mdx#metric-definitions)', 'Use the metric contracts below');
const original = body.replace('Use the [metric definitions](./index.mdx#metric-definitions)', 'Use the metric contracts below')
// The old navigation named nonexistent roadmap gates. Restore only that
// historical sentence for the migration hash; metric substance stays exact.
.replace('Use these safeguards throughout the [Stage 01 journey](../stage-01-skills-adoption.mdx)', 'Introduction depends on the [roadmap gates](../roadmap.mdx)');
assert.equal(createHash('sha256').update(original).digest('hex'), part.sha256);
}
});
+3 -1
View File
@@ -10,7 +10,9 @@ test('Skills Measurement is a visible optional registry, separate from workflow
for (const heading of ['Establish the baseline', 'Measure the pilot', 'Extend collection as adoption grows', 'Keep the collection running']) assert(!page.includes(heading), heading);
assert(page.includes('Registry inclusion does not require a metric to be used, collected or baselined'));
assert(page.includes('../baseline.mdx'));
assert(!page.includes('<details>'));
assert(page.includes('<summary>Implementation status—not live telemetry</summary>'));
const visible = page.replace(/<details>[\s\S]*?<\/details>/g, '');
assert.equal([...visible.matchAll(/^\d+\. \[/gm)].length,16,'All contract links remain visible');
assert(page.includes('./collection-and-safeguards.mdx'));
assert(!page.includes('Is AI-assisted work worth continuing?'));
for (const word of ['candidate', 'not installed', 'applicability', 'calculation', 'inputs', 'limitations', 'implementation status']) assert(page.includes(word), word);
+4 -4
View File
@@ -7,14 +7,14 @@ import {execFileSync} from 'node:child_process';
import {skillsViewer,isSkillsStory} from './skills-viewer.mjs';
const read = p => readFileSync(p,'utf8');
const topics = ['measurement','baseline','governance','quality','adoption','maintenance'];
test('all seven Skills Adoption pages account for six diagrams and twelve sources',()=>{
test('all seven Skills Adoption pages include their own native diagram story',()=>{
let count=0;
for(const route of ['stage-01-skills-adoption',...topics.map(t=>['measurement','baseline'].includes(t)?t:`skills-${t}`)]){
const diagrams=[...read(`docs/strategy/${route === 'measurement' ? 'measurement/index' : route}.mdx`).matchAll(/<ArchifyDiagram name="([^"]+)"/g)];
assert.equal(diagrams.length,route.startsWith('stage-')?0:1);
assert.equal(diagrams.length,1);
for(const [,name] of diagrams){assert(isSkillsStory(name));for(const suffix of ['','-mobile'])JSON.parse(read(`diagrams/${name}${suffix}.architecture.json`));count++;}
}
assert.equal(count,6);assert(!isSkillsStory('strategy-target'));assert(!isSkillsStory('strategy-roadmap'));
assert.equal(count,7);assert(!isSkillsStory('strategy-target'));assert(!isSkillsStory('strategy-roadmap'));
});
for(const topic of topics) test(`${topic}: rich native source, wide desktop, mobile parity and checked adapter`,()=>{
const name=`strategy-${topic}`;const desktop=JSON.parse(read(`diagrams/${name}.architecture.json`));
@@ -43,7 +43,7 @@ test('domain contracts retain human authority, honest measures and Delivery-firs
assert(text('quality').includes('Critical effect blocks'));
assert(text('adoption').includes('Delivery first')&&text('adoption').includes('Later order undecided'));
assert(text('adoption').includes('No quotas or rankings'));
for(const marker of ['Workflow effectiveness','Skills effectiveness & health','16 optional candidate contracts','No mandatory collection','Before / after is not causality','9 prototype / 7 unimplemented'])assert(text('measurement').includes(marker),marker);
for(const marker of ['Workflow effectiveness','Skills effectiveness & health','Quality: artifact fitness + safety','No mandatory collection','Before / after is not causality','Candidate / approved regression'])assert(text('measurement').includes(marker),marker);
assert(text('maintenance').includes('No automatic reinstatement'));
});
test('topic compositions explain distinct relationships rather than a fixed card count',()=>{
+4 -3
View File
@@ -1,9 +1,10 @@
// Native editable geometry and edges; scoped editorial typography for Stage 01.
export const isSkillsStory = name => /^strategy-(governance|quality|adoption|measurement|baseline|maintenance)(-mobile)?$/.test(name);
export const isSkillsStory = name => /^strategy-(governance|quality|adoption|measurement|baseline|maintenance|stage01|stage02)(-mobile)?$/.test(name);
const escape = text => text.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;');
export function skillsViewer(html, spec) {
if (spec.diagram_type !== 'architecture' || !spec.components.length) throw new Error('Skills story contract drift');
const detailSize = spec.meta.output.includes('-mobile/') ? 34 : 32;
const stageMobile = /strategy-stage0[12]-mobile\//.test(spec.meta.output);
const detailSize = stageMobile ? 38 : spec.meta.output.includes('-mobile/') ? 34 : 32;
for (const node of spec.components) {
const pattern = new RegExp(`<g\\b[^>]*data-node-id="${node.id}"[^>]*>[\\s\\S]*?<text data-node-label=[\\s\\S]*?</g>`);
const match = html.match(pattern);
@@ -16,7 +17,7 @@ export function skillsViewer(html, spec) {
if (!node.tag || !node.sublabel || lines.length > 4) throw new Error(`Skills copy exceeds capacity: ${node.id}`);
const text = (value, top, size, attrs) => `<text x="${x+28}" y="${y+top}" font-size="${size}" ${attrs}>${escape(value)}</text>`;
const content = text(node.tag, 40, detailSize, 'class="t-muted" data-detail="context"')
+ text(node.label, 89, 36, 'class="t-primary" data-node-label="" font-weight="700"')
+ text(node.label, 89, stageMobile ? 40 : 36, 'class="t-primary" data-node-label="" font-weight="700"')
+ lines.map((line,i) => text(line, 138+i*42, detailSize, 'class="t-primary" data-detail="context"')).join('');
const prefix = original.slice(0,rectangles[1].index+rectangles[1][0].length).replaceAll('rx="6"','rx="0"');
html = html.replace(original,prefix+content+'</g>');
+4 -4
View File
@@ -13,12 +13,12 @@ test('every strategy page leads with a compact diagram story and closes its refe
assert(!page.includes('<details>') || page.indexOf(' story />') < page.indexOf('<details>'), id);
assert(!/<details\s+open/.test(page), id);
assert.equal((page.match(/<details>/g) || []).length, (page.match(/<\/details>/g) || []).length, id);
assert.equal((narrative.match(/^## /gm) || []).length, id === 'measurement' ? 2 : id === 'roadmap' ? 4 : 1, id);
assert((narrative.match(/^- /gm) || []).length <= 3, id);
assert(narrative.includes('**Next:**'), id);
assert((narrative.match(/^## /gm) || []).length <= 4, id);
assert((narrative.match(/^- /gm) || []).length <= 7, id);
assert(/\*\*(Next|Continue the loop):\*\*/.test(narrative), id);
assert(!narrative.includes('| ---'), id);
const words = narrative.replace(/<[^>]*>/g, '').trim().split(/\s+/).length;
assert(words <= (id === 'measurement' ? 1500 : id === 'roadmap' ? 600 : 200), `${id}: ${words} default narrative words`);
assert(words <= (id === 'measurement' ? 1600 : id === 'roadmap' ? 600 : 500), `${id}: ${words} default narrative words`);
}
});