diff --git a/diagrams/strategy-target-mobile.architecture.json b/diagrams/strategy-target-mobile.architecture.json
index f1a4580..c311370 100644
--- a/diagrams/strategy-target-mobile.architecture.json
+++ b/diagrams/strategy-target-mobile.architecture.json
@@ -3,25 +3,50 @@
"diagram_type": "architecture",
"meta": {"title": "TO-BE: autonomous SDLC, human direction", "output": "static/diagrams/strategy-target-mobile/index.html", "quality_profile": "showcase", "legend": {"mode": "hidden"}},
"components": [
- {"id": "humans", "type": "backend", "label": "Humans", "sublabel": "Direct, guide and review", "pos": [20, 20], "size": [240, 80]},
- {"id": "agent", "type": "security", "label": "Autonomous agent", "sublabel": "Executes and governs SDLC", "pos": [20, 180], "size": [240, 80]},
- {"id": "scope", "type": "backend", "label": "Scope", "sublabel": "01", "pos": [20, 340], "size": [240, 80]},
- {"id": "architecture", "type": "backend", "label": "Architecture", "sublabel": "02", "pos": [20, 500], "size": [240, 80]},
- {"id": "ui-ux", "type": "backend", "label": "UI/UX", "sublabel": "03", "pos": [20, 660], "size": [240, 80]},
- {"id": "planning", "type": "backend", "label": "Planning", "sublabel": "04", "pos": [20, 820], "size": [240, 80]},
- {"id": "delivery", "type": "backend", "label": "Delivery", "sublabel": "05", "pos": [20, 980], "size": [240, 80]},
- {"id": "uat", "type": "backend", "label": "UAT", "sublabel": "06", "pos": [20, 1140], "size": [240, 80]},
- {"id": "releases", "type": "backend", "label": "Releases", "sublabel": "07", "pos": [20, 1300], "size": [240, 80]}
+ {"id":"humans","type":"backend","label":"Humans","sublabel":"Direct, guide and review","pos":[140,20],"size":[360,100]},
+ {"id":"agent","type":"security","label":"Autonomous agent","sublabel":"Executes / governs SDLC","pos":[140,200],"size":[360,120]},
+ {"id":"scope","type":"backend","label":"Scope","sublabel":"01","pos":[100,440],"size":[240,100]},
+ {"id":"architecture","type":"backend","label":"Architecture","sublabel":"02","pos":[100,660],"size":[240,100]},
+ {"id":"ui-ux","type":"backend","label":"UI/UX","sublabel":"03","pos":[100,880],"size":[240,100]},
+ {"id":"planning","type":"backend","label":"Planning","sublabel":"04","pos":[100,1100],"size":[240,100]},
+ {"id":"delivery","type":"backend","label":"Delivery","sublabel":"05","pos":[100,1320],"size":[240,100]},
+ {"id":"uat","type":"backend","label":"UAT","sublabel":"06","pos":[100,1540],"size":[240,100]},
+ {"id":"security","type":"backend","label":"Security","sublabel":"07","pos":[100,1760],"size":[240,100]},
+ {"id":"releases","type":"backend","label":"Releases","sublabel":"08","pos":[100,1980],"size":[240,100]},
+ {"id":"code-scope","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,440],"size":[240,100]},
+ {"id":"code-architecture","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,660],"size":[240,100]},
+ {"id":"code-ui-ux","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,880],"size":[240,100]},
+ {"id":"code-planning","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,1100],"size":[240,100]},
+ {"id":"code-delivery","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,1320],"size":[240,100]},
+ {"id":"code-uat","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,1540],"size":[240,100]},
+ {"id":"code-security","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,1760],"size":[240,100]},
+ {"id":"code-releases","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[400,1980],"size":[240,100]}
],
"connections": [
- {"id": "human-direction", "from": "humans", "to": "agent", "label": "Guide", "fromSide": "bottom", "toSide": "top"},
- {"id": "agent-lifecycle", "from": "agent", "to": "scope", "label": "Entire cycle", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-1", "from": "scope", "to": "architecture", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-2", "from": "architecture", "to": "ui-ux", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-3", "from": "ui-ux", "to": "planning", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-4", "from": "planning", "to": "delivery", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-5", "from": "delivery", "to": "uat", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-6", "from": "uat", "to": "releases", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-7", "from": "releases", "to": "scope", "label": "Repeat indefinitely", "fromSide": "right", "toSide": "right", "via": [[300, 1340], [300, 380]], "labelAt": [180, 1430]}
+ {"id":"human-direction","from":"humans","to":"agent","label":"Guide","fromSide":"bottom","toSide":"top"},
+ {"id":"govern-scope","from":"agent","to":"scope","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,490]]},
+ {"id":"govern-architecture","from":"agent","to":"architecture","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,710]]},
+ {"id":"govern-ui-ux","from":"agent","to":"ui-ux","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,930]]},
+ {"id":"govern-planning","from":"agent","to":"planning","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,1150]]},
+ {"id":"govern-delivery","from":"agent","to":"delivery","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,1370]]},
+ {"id":"govern-uat","from":"agent","to":"uat","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,1590]]},
+ {"id":"govern-security","from":"agent","to":"security","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,1810]]},
+ {"id":"govern-releases","from":"agent","to":"releases","label":"","variant":"security","fromSide":"left","toSide":"left","via":[[60,260],[60,2030]]},
+ {"id":"stage-1","from":"scope","to":"architecture","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-2","from":"architecture","to":"ui-ux","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-3","from":"ui-ux","to":"planning","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-4","from":"planning","to":"delivery","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-5","from":"delivery","to":"uat","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-6","from":"uat","to":"security","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-7","from":"security","to":"releases","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-8","from":"releases","to":"scope","label":"Repeat indefinitely","variant":"emphasis","fromSide":"bottom","toSide":"top","via":[[220,2140],[680,2140],[680,380],[220,380]],"labelAt":[440,2180]},
+ {"id":"contribute-scope","from":"code-scope","to":"scope","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-architecture","from":"code-architecture","to":"architecture","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-ui-ux","from":"code-ui-ux","to":"ui-ux","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-planning","from":"code-planning","to":"planning","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-delivery","from":"code-delivery","to":"delivery","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-uat","from":"code-uat","to":"uat","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-security","from":"code-security","to":"security","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"},
+ {"id":"contribute-releases","from":"code-releases","to":"releases","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right"}
]
}
\ No newline at end of file
diff --git a/diagrams/strategy-target.architecture.json b/diagrams/strategy-target.architecture.json
index 5f12e77..a1ab490 100644
--- a/diagrams/strategy-target.architecture.json
+++ b/diagrams/strategy-target.architecture.json
@@ -3,25 +3,50 @@
"diagram_type": "architecture",
"meta": {"title": "TO-BE: autonomous SDLC, human direction", "output": "static/diagrams/strategy-target/index.html", "quality_profile": "showcase", "legend": {"mode": "hidden"}},
"components": [
- {"id": "humans", "type": "backend", "label": "Humans", "sublabel": "Direct, guide and review", "pos": [40, 20], "size": [240, 80]},
- {"id": "agent", "type": "security", "label": "Autonomous agent", "sublabel": "Executes and governs SDLC", "pos": [360, 20], "size": [240, 80]},
- {"id": "scope", "type": "backend", "label": "Scope", "sublabel": "01", "pos": [40, 220], "size": [240, 80]},
- {"id": "architecture", "type": "backend", "label": "Architecture", "sublabel": "02", "pos": [360, 220], "size": [240, 80]},
- {"id": "ui-ux", "type": "backend", "label": "UI/UX", "sublabel": "03", "pos": [680, 220], "size": [240, 80]},
- {"id": "planning", "type": "backend", "label": "Planning", "sublabel": "04", "pos": [680, 380], "size": [240, 80]},
- {"id": "delivery", "type": "backend", "label": "Delivery", "sublabel": "05", "pos": [680, 540], "size": [240, 80]},
- {"id": "uat", "type": "backend", "label": "UAT", "sublabel": "06", "pos": [360, 540], "size": [240, 80]},
- {"id": "releases", "type": "backend", "label": "Releases", "sublabel": "07", "pos": [40, 540], "size": [240, 80]}
+ {"id":"humans","type":"backend","label":"Humans","sublabel":"Direct, guide and review","pos":[465,322.5],"size":[270,75]},
+ {"id":"agent","type":"security","label":"Autonomous agent","sublabel":"Executes / governs SDLC","pos":[465,450],"size":[270,90]},
+ {"id":"scope","type":"backend","label":"Scope","sublabel":"01","pos":[510,187.5],"size":[180,75]},
+ {"id":"architecture","type":"backend","label":"Architecture","sublabel":"02","pos":[780,187.5],"size":[180,75]},
+ {"id":"ui-ux","type":"backend","label":"UI/UX","sublabel":"03","pos":[780,457.5],"size":[180,75]},
+ {"id":"planning","type":"backend","label":"Planning","sublabel":"04","pos":[780,727.5],"size":[180,75]},
+ {"id":"delivery","type":"backend","label":"Delivery","sublabel":"05","pos":[510,727.5],"size":[180,75]},
+ {"id":"uat","type":"backend","label":"UAT","sublabel":"06","pos":[240,727.5],"size":[180,75]},
+ {"id":"security","type":"backend","label":"Security","sublabel":"07","pos":[240,457.5],"size":[180,75]},
+ {"id":"releases","type":"backend","label":"Releases","sublabel":"08","pos":[240,187.5],"size":[180,75]},
+ {"id":"code-scope","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[510,37.5],"size":[180,75]},
+ {"id":"code-architecture","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[780,37.5],"size":[180,75]},
+ {"id":"code-ui-ux","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[1005,457.5],"size":[180,75]},
+ {"id":"code-planning","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[780,877.5],"size":[180,75]},
+ {"id":"code-delivery","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[510,877.5],"size":[180,75]},
+ {"id":"code-uat","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[240,877.5],"size":[180,75]},
+ {"id":"code-security","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[15,457.5],"size":[180,75]},
+ {"id":"code-releases","type":"frontend","label":"Code agent","sublabel":"Human-controlled","pos":[240,37.5],"size":[180,75]}
],
"connections": [
- {"id": "human-direction", "from": "humans", "to": "agent", "label": "Guide", "fromSide": "right", "toSide": "left"},
- {"id": "agent-lifecycle", "from": "agent", "to": "scope", "label": "Entire cycle", "fromSide": "bottom", "toSide": "top", "via": [[480, 160], [160, 160]], "labelAt": [320, 145]},
- {"id": "stage-1", "from": "scope", "to": "architecture", "label": "Then", "fromSide": "right", "toSide": "left"},
- {"id": "stage-2", "from": "architecture", "to": "ui-ux", "label": "Then", "fromSide": "right", "toSide": "left"},
- {"id": "stage-3", "from": "ui-ux", "to": "planning", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-4", "from": "planning", "to": "delivery", "label": "Then", "fromSide": "bottom", "toSide": "top"},
- {"id": "stage-5", "from": "delivery", "to": "uat", "label": "Then", "fromSide": "left", "toSide": "right"},
- {"id": "stage-6", "from": "uat", "to": "releases", "label": "Then", "fromSide": "left", "toSide": "right"},
- {"id": "stage-7", "from": "releases", "to": "scope", "label": "Repeat indefinitely", "fromSide": "top", "toSide": "bottom", "via": [[160, 460], [160, 380]], "labelAt": [260, 420]}
+ {"id":"human-direction","from":"humans","to":"agent","label":"Guide","fromSide":"bottom","toSide":"top"},
+ {"id":"govern-scope","from":"agent","to":"scope","label":"","variant":"security","fromSide":"right","toSide":"bottom","via":[[750,495],[750,292.5],[600,292.5]]},
+ {"id":"govern-architecture","from":"agent","to":"architecture","label":"","variant":"security","fromSide":"right","toSide":"bottom","via":[[765,495],[765,345],[870,345]]},
+ {"id":"govern-ui-ux","from":"agent","to":"ui-ux","label":"","variant":"security","fromSide":"right","toSide":"left"},
+ {"id":"govern-planning","from":"agent","to":"planning","label":"","variant":"security","fromSide":"right","toSide":"top","via":[[765,495],[765,690],[870,690]]},
+ {"id":"govern-delivery","from":"agent","to":"delivery","label":"","variant":"security","fromSide":"bottom","toSide":"top"},
+ {"id":"govern-uat","from":"agent","to":"uat","label":"","variant":"security","fromSide":"left","toSide":"top","via":[[435,495],[435,690],[330,690]]},
+ {"id":"govern-security","from":"agent","to":"security","label":"","variant":"security","fromSide":"left","toSide":"right"},
+ {"id":"govern-releases","from":"agent","to":"releases","label":"","variant":"security","fromSide":"left","toSide":"bottom","via":[[435,495],[435,345],[330,345]]},
+ {"id":"stage-1","from":"scope","to":"architecture","label":"Then","variant":"emphasis","fromSide":"right","toSide":"left"},
+ {"id":"stage-2","from":"architecture","to":"ui-ux","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-3","from":"ui-ux","to":"planning","label":"Then","variant":"emphasis","fromSide":"bottom","toSide":"top"},
+ {"id":"stage-4","from":"planning","to":"delivery","label":"Then","variant":"emphasis","fromSide":"left","toSide":"right"},
+ {"id":"stage-5","from":"delivery","to":"uat","label":"Then","variant":"emphasis","fromSide":"left","toSide":"right"},
+ {"id":"stage-6","from":"uat","to":"security","label":"Then","variant":"emphasis","fromSide":"top","toSide":"bottom"},
+ {"id":"stage-7","from":"security","to":"releases","label":"Then","variant":"emphasis","fromSide":"top","toSide":"bottom"},
+ {"id":"stage-8","from":"releases","to":"scope","label":"Repeat indefinitely","variant":"emphasis","fromSide":"right","toSide":"left","labelAt":[465,170]},
+ {"id":"contribute-scope","from":"code-scope","to":"scope","label":"Contribute","variant":"dashed","fromSide":"bottom","toSide":"top"},
+ {"id":"contribute-architecture","from":"code-architecture","to":"architecture","label":"Contribute","variant":"dashed","fromSide":"bottom","toSide":"top"},
+ {"id":"contribute-ui-ux","from":"code-ui-ux","to":"ui-ux","label":"Contribute","variant":"dashed","fromSide":"left","toSide":"right","labelAt":[990,570]},
+ {"id":"contribute-planning","from":"code-planning","to":"planning","label":"Contribute","variant":"dashed","fromSide":"top","toSide":"bottom"},
+ {"id":"contribute-delivery","from":"code-delivery","to":"delivery","label":"Contribute","variant":"dashed","fromSide":"top","toSide":"bottom"},
+ {"id":"contribute-uat","from":"code-uat","to":"uat","label":"Contribute","variant":"dashed","fromSide":"top","toSide":"bottom"},
+ {"id":"contribute-security","from":"code-security","to":"security","label":"Contribute","variant":"dashed","fromSide":"right","toSide":"left","labelAt":[220,570]},
+ {"id":"contribute-releases","from":"code-releases","to":"releases","label":"Contribute","variant":"dashed","fromSide":"bottom","toSide":"top"}
]
}
\ No newline at end of file
diff --git a/docs/strategy/index.mdx b/docs/strategy/index.mdx
index 96187fb..b0b19e7 100644
--- a/docs/strategy/index.mdx
+++ b/docs/strategy/index.mdx
@@ -16,9 +16,11 @@ import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
-An autonomous agent executes and governs the SDLC. Humans direct, guide and review the work throughout the cycle.
+An autonomous agent executes and governs every phase. Humans direct, guide and review it; a separate human-controlled code agent contributes to each phase from outside the cycle.
-**Scope → Architecture → UI/UX → Planning → Delivery → UAT → Releases → Scope**
+**Solid arrows:** lifecycle · **Dotted spokes:** execution and governance · **Dashed arrows:** contributions.
+
+**Scope → Architecture → UI/UX → Planning → Delivery → UAT → Security → Releases → Scope**
Releases returns to Scope and the cycle repeats indefinitely. Governed skills and measured outcomes support the path toward this target; they are not a substitute for it.
diff --git a/docs/strategy/target-operating-model.mdx b/docs/strategy/target-operating-model.mdx
index b761e03..11836a9 100644
--- a/docs/strategy/target-operating-model.mdx
+++ b/docs/strategy/target-operating-model.mdx
@@ -1,6 +1,7 @@
---
id: target-operating-model
title: Target operating model
+sidebar_label: Target
description: An autonomous agent executes and governs the SDLC; humans direct, guide and review the repeating cycle.
hide_table_of_contents: true
---
@@ -15,9 +16,11 @@ import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
-An autonomous agent executes and governs the SDLC. Humans direct, guide and review the work throughout the cycle.
+An autonomous agent executes and governs every phase. Humans direct, guide and review it; a separate human-controlled code agent contributes to each phase from outside the cycle.
-**Scope → Architecture → UI/UX → Planning → Delivery → UAT → Releases → Scope**
+**Solid arrows:** lifecycle · **Dotted spokes:** execution and governance · **Dashed arrows:** contributions.
+
+**Scope → Architecture → UI/UX → Planning → Delivery → UAT → Security → Releases → Scope**
Each stage follows the previous one. Releases returns to Scope; the cycle repeats indefinitely, with human direction informing the next iteration.
@@ -44,7 +47,7 @@ The autonomous agent is more than a task router: it executes and governs the rep
One logical coordinator does not mean one fragile process. A durable task ledger, explicit state transitions, leases and idempotency keys allow recovery or multiple replicas without duplicate delivery. Inference outages stop new agent decisions, not the organization's ability to take over. People can resume work through the normal deterministic workflow. Evidence and authorization services fail closed for privileged actions; missing evidence never becomes permission.
-These controls support the sequence Scope → Architecture → UI/UX → Planning → Delivery → UAT → Releases → Scope; they do not replace it with a separate lifecycle. Product, engineering, design, quality, security and platform expertise inform human direction and review. Specific approval assignments and release permissions are implementation decisions still to be defined.
+These controls support the sequence Scope → Architecture → UI/UX → Planning → Delivery → UAT → Security → Releases → Scope; they do not replace it with a separate lifecycle. Product, engineering, design, quality, security and platform expertise inform human direction and review. Specific approval assignments and release permissions are implementation decisions still to be defined.
### Trust and authority boundaries
diff --git a/scripts/browser/inline-controls.cjs b/scripts/browser/inline-controls.cjs
index 6b6aa5f..2a193fb 100644
--- a/scripts/browser/inline-controls.cjs
+++ b/scripts/browser/inline-controls.cjs
@@ -88,7 +88,7 @@ const fs = require('node:fs');
if (format.startsWith('svg')) {
const valid = await page.evaluate(xml => {
const doc = new DOMParser().parseFromString(xml, 'image/svg+xml');
- return !doc.querySelector('parsererror') && doc.documentElement.localName === 'svg' && doc.querySelectorAll('text').length > 0;
+ return !doc.querySelector('parsererror') && doc.documentElement.localName === 'svg' && doc.querySelectorAll('text').length > 0 && doc.querySelector('#eldorado-cycle-export')?.textContent.includes('stroke-dasharray: 3 9');
}, bytes.toString());
assert(valid, format);
} else {
@@ -117,10 +117,13 @@ const fs = require('node:fs');
await close.focus(); await page.keyboard.press('Escape');
assert(await opener.evaluate(n => n === document.activeElement));
await iframe.scrollIntoViewIfNeeded();
- await page.evaluate(() => scrollTo(0, 0));
+ await page.evaluate(() => scrollTo({top: 0, behavior: 'instant'}));
+ await page.evaluate(() => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
await page.screenshot({path:`${output}/${width}.png`, fullPage:true});
await frame.locator('#btn-theme').click(); await theme('dark');
- await page.locator('.strategy-content a[href$="/strategy/target-operating-model/"]').filter({visible:true}).click();
+ // Exercise keyboard navigation without racing the portal's smooth scroll.
+ await page.locator('.strategy-content a[href$="/strategy/target-operating-model/"]').filter({visible:true}).focus();
+ await page.keyboard.press('Enter');
await page.waitForURL('**/strategy/target-operating-model/');
await page.getByRole('heading', {name:'Target operating model', exact:true, level:1}).waitFor();
const next = page.locator(`.archify-panel--story[data-diagram="strategy-target"] > .story-${width > 700 ? 'desktop' : 'mobile'}`);
diff --git a/scripts/browser/sdlc-cycle.cjs b/scripts/browser/sdlc-cycle.cjs
index 6faf892..fa4a729 100644
--- a/scripts/browser/sdlc-cycle.cjs
+++ b/scripts/browser/sdlc-cycle.cjs
@@ -19,7 +19,7 @@ const fs = require('node:fs');
await page.waitForURL('**/strategy/target-operating-model/');
for (const route of ['target-operating-model/', '']) {
await page.goto(`${base}/eldorado-v1/documentation/strategy/${route}`, {waitUntil:'networkidle'});
- if (width === 1440) assert.equal(await page.locator('.theme-doc-sidebar-menu .menu__link').first().innerText(), 'Target operating model');
+ if (width === 1440) assert.equal(await page.locator('.theme-doc-sidebar-menu .menu__link').first().innerText(), 'Target');
const iframe = page.locator(`.archify-panel--story > .story-${width > 700 ? 'desktop' : 'mobile'}`);
await iframe.scrollIntoViewIfNeeded();
const frame = await (await iframe.elementHandle()).contentFrame();
@@ -32,15 +32,36 @@ const fs = require('node:fs');
const box = n.querySelector('rect').getBoundingClientRect();
return [...n.querySelectorAll('text')].filter(t => {const b=t.getBoundingClientRect();return b.leftbox.right||b.topbox.bottom;}).map(t=>t.textContent);
});
- return {labels,edges,overflow};
+ const relationships = [...svg.querySelectorAll('path[data-edge-id]')].map(n => ({id:n.dataset.edgeId,from:n.dataset.edgeFrom,to:n.dataset.edgeTo,dash:getComputedStyle(n).strokeDasharray,arrow:n.getAttribute('marker-end')}));
+ const nodes = [...svg.querySelectorAll('[data-node-id]')].map(n => {const r=n.querySelector('rect').getBoundingClientRect();return {id:n.dataset.nodeId,x:r.x+r.width/2,y:r.y+r.height/2};});
+ return {labels,edges,overflow,relationships,nodes};
});
- const ids = ['scope','architecture','ui-ux','planning','delivery','uat','releases'];
+ const ids = ['scope','architecture','ui-ux','planning','delivery','uat','security','releases'];
assert.deepEqual(metrics.edges.map(e=>e.slice(0,2)), ids.map((id,i)=>[id,ids[(i+1)%ids.length]]));
assert(metrics.edges.every(e=>e[2]?.startsWith('url(')), 'all stages must have arrowheads');
+ assert.equal(metrics.nodes.length, 18);
+ const governs = metrics.relationships.filter(e=>e.id.startsWith('govern-'));
+ const contributions = metrics.relationships.filter(e=>e.id.startsWith('contribute-'));
+ assert.deepEqual(governs.map(e=>[e.from,e.to]), ids.map(id=>['agent',id]));
+ assert.deepEqual(contributions.map(e=>[e.from,e.to]), ids.map(id=>[`code-${id}`,id]));
+ assert(governs.every(e=>e.dash==='3px, 9px' && e.arrow?.startsWith('url(')));
+ assert(contributions.every(e=>e.dash==='12px, 8px' && e.arrow?.startsWith('url(')));
+ assert(metrics.relationships.some(e=>e.from==='humans' && e.to==='agent'));
+ if (width > 700) {
+ const agent = metrics.nodes.find(n=>n.id==='agent');
+ const phases = metrics.nodes.filter(n=>ids.includes(n.id));
+ for (const axis of ['x','y']) assert(Math.abs(agent[axis]-(Math.min(...phases.map(n=>n[axis]))+Math.max(...phases.map(n=>n[axis])))/2)<1);
+ for (const id of ids) {
+ const phase=phases.find(n=>n.id===id), code=metrics.nodes.find(n=>n.id===`code-${id}`);
+ assert(Math.hypot(code.x-agent.x,code.y-agent.y)>Math.hypot(phase.x-agent.x,phase.y-agent.y));
+ }
+ }
assert(metrics.labels.every(n=>n.px>=14), JSON.stringify(metrics.labels));
assert.deepEqual(metrics.overflow, []);
assert(!await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth));
- await page.evaluate(()=>scrollTo(0,0));
+ await page.evaluate(()=>scrollTo({top:0,behavior:'instant'}));
+ await page.waitForFunction(()=>scrollY===0);
+ await page.evaluate(()=>new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve))));
await page.screenshot({path:`${output}/${width}-${route ? 'target' : 'landing'}.png`,fullPage:true});
results.push({width,route,...metrics});
fs.writeFileSync(`${output}/results.json`,JSON.stringify(results,null,2));
diff --git a/scripts/browser/strategy-story.cjs b/scripts/browser/strategy-story.cjs
index 3b64422..3d8fe7b 100644
--- a/scripts/browser/strategy-story.cjs
+++ b/scripts/browser/strategy-story.cjs
@@ -19,18 +19,21 @@ const fs=require('node:fs');
const nodes=[...svg.querySelectorAll('[data-node-id]')].map(n=>{const r=n.getBoundingClientRect();return {id:n.id,x:r.x,y:r.y,right:r.right,bottom:r.bottom};});
return {width:innerWidth,height:innerHeight,nodes,svg:svg.getBoundingClientRect().toJSON(),minLabel:Math.min(...[...svg.querySelectorAll('text[data-node-label]')].map(n=>parseFloat(getComputedStyle(n).fontSize)*n.getScreenCTM().a))};
});
- assert(geometry.nodes.length>=5 && geometry.nodes.length<=(['index','target-operating-model'].includes(id)?9:7),id);
+ assert(geometry.nodes.length>=5 && geometry.nodes.length<=(['index','target-operating-model'].includes(id)?18:7),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<=200,id);assert(!data.overflow,id);assert.equal(data.openDetails,0,id);
- await page.evaluate(()=>scrollTo(0,0));await page.screenshot({path:`.cache/story-${label}/${width}-${id}.png`,fullPage:true});
+ 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'});
assert.equal(await close.innerText(),'×');assert(await close.evaluate(n=>n===document.activeElement));
const box=await close.boundingBox();assert.equal(box.width,44);assert.equal(box.height,44);
await close.click();assert(await opener.evaluate(n=>n===document.activeElement));
await opener.click();await close.waitFor({state:'visible'});await page.keyboard.press('Escape');await page.waitForFunction(()=>!document.querySelector('.archify-panel--story dialog').open);assert(await opener.evaluate(n=>n===document.activeElement));
- const summary=page.locator('.strategy-content > details > summary').first();await summary.click();assert(await summary.evaluate(n=>n.parentElement.open));await summary.click();
+ 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);
+ await page.keyboard.press('Enter');
results.push({id,width,...data,geometry,modalClick:true,modalEscape:true,focusRestored:true,details:true});fs.writeFileSync(`.cache/story-${label}/results.json`,JSON.stringify(results,null,2));
}
await page.goto(`${base}/eldorado-v1/documentation/strategy/measurement/#validated-task-success`,{waitUntil:'networkidle'});
diff --git a/scripts/build-diagrams.mjs b/scripts/build-diagrams.mjs
index b27a332..72094cd 100644
--- a/scripts/build-diagrams.mjs
+++ b/scripts/build-diagrams.mjs
@@ -37,7 +37,15 @@ for (const file of readdirSync('diagrams').sort()) {
if (start < 0 || end < start || preset.split(guard).length !== 3) throw new Error('Archify preset integration drift');
viewer = viewer.slice(0, start) + preset.replaceAll(guard, '') + viewer.slice(end);
if (!viewer.includes('') || !viewer.includes('