From 29beebb3799ea69fa7801eb92344038cb4b6da43 Mon Sep 17 00:00:00 2001 From: Jarvis Jr Hermes Date: Mon, 5 Oct 2026 16:32:51 +0000 Subject: [PATCH] docs: lead Strategy with autonomous SDLC cycle --- .../strategy-target-mobile.architecture.json | 167 ++---------------- diagrams/strategy-target.architecture.json | 167 ++---------------- docs/strategy/index.mdx | 20 +-- docs/strategy/target-operating-model.mdx | 40 +++-- package.json | 2 +- scripts/browser/inline-controls.cjs | 2 + scripts/browser/sdlc-cycle.cjs | 53 ++++++ scripts/browser/strategy-story.cjs | 2 +- scripts/build-diagrams.mjs | 4 +- scripts/cycle-viewer.css | 14 ++ scripts/sdlc-cycle.test.mjs | 36 ++++ scripts/strategy-story.test.mjs | 4 +- sidebars/strategy.ts | 2 +- src/css/custom.css | 4 +- 14 files changed, 184 insertions(+), 333 deletions(-) create mode 100644 scripts/browser/sdlc-cycle.cjs create mode 100644 scripts/cycle-viewer.css create mode 100644 scripts/sdlc-cycle.test.mjs diff --git a/diagrams/strategy-target-mobile.architecture.json b/diagrams/strategy-target-mobile.architecture.json index 5b4e467..f1a4580 100644 --- a/diagrams/strategy-target-mobile.architecture.json +++ b/diagrams/strategy-target-mobile.architecture.json @@ -1,156 +1,27 @@ { "schema_version": 1, "diagram_type": "architecture", - "meta": { - "title": "TO-BE: coordination without authority transfer", - "output": "static/diagrams/strategy-target-mobile/index.html", - "quality_profile": "showcase", - "legend": { - "mode": "hidden" - } - }, + "meta": {"title": "TO-BE: autonomous SDLC, human direction", "output": "static/diagrams/strategy-target-mobile/index.html", "quality_profile": "showcase", "legend": {"mode": "hidden"}}, "components": [ - { - "id": "people", - "type": "backend", - "label": "People authorize", - "sublabel": "Retain accountability", - "pos": [ - 40, - 20 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "coordinator", - "type": "backend", - "label": "SDLC coordinator", - "sublabel": "Routes approved work", - "pos": [ - 40, - 260 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "clients", - "type": "backend", - "label": "Local coding agents", - "sublabel": "Propose changes", - "pos": [ - 40, - 380 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "gates", - "type": "security", - "label": "Repository + CI", - "sublabel": "Verify exact revision", - "pos": [ - 40, - 500 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "registry", - "type": "backend", - "label": "Governed skills", - "sublabel": "Pinned + qualified", - "pos": [ - 40, - 140 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "approval", - "type": "security", - "label": "Human approval", - "sublabel": "Exact artifact + scope", - "pos": [ - 40, - 620 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "release", - "type": "security", - "label": "Release service", - "sublabel": "Production credentials", - "pos": [ - 40, - 740 - ], - "size": [ - 180, - 60 - ] - } + {"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]} ], "connections": [ - { - "id": "flow-0", - "from": "people", - "to": "coordinator", - "label": "Authorize scope" - }, - { - "id": "flow-1", - "from": "coordinator", - "to": "clients", - "label": "Route tasks" - }, - { - "id": "flow-2", - "from": "registry", - "to": "coordinator", - "label": "Guide" - }, - { - "id": "flow-3", - "from": "registry", - "to": "clients", - "label": "Guide" - }, - { - "id": "flow-4", - "from": "clients", - "to": "gates", - "label": "Propose" - }, - { - "id": "flow-5", - "from": "gates", - "to": "approval", - "label": "Evidence" - }, - { - "id": "flow-6", - "from": "approval", - "to": "release", - "label": "Authorize" - } + {"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]} ] } \ No newline at end of file diff --git a/diagrams/strategy-target.architecture.json b/diagrams/strategy-target.architecture.json index d7605e4..5f12e77 100644 --- a/diagrams/strategy-target.architecture.json +++ b/diagrams/strategy-target.architecture.json @@ -1,156 +1,27 @@ { "schema_version": 1, "diagram_type": "architecture", - "meta": { - "title": "TO-BE: coordination without authority transfer", - "output": "static/diagrams/strategy-target/index.html", - "quality_profile": "showcase", - "legend": { - "mode": "hidden" - } - }, + "meta": {"title": "TO-BE: autonomous SDLC, human direction", "output": "static/diagrams/strategy-target/index.html", "quality_profile": "showcase", "legend": {"mode": "hidden"}}, "components": [ - { - "id": "people", - "type": "backend", - "label": "People authorize", - "sublabel": "Retain accountability", - "pos": [ - 20, - 20 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "coordinator", - "type": "backend", - "label": "SDLC coordinator", - "sublabel": "Routes approved work", - "pos": [ - 20, - 140 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "clients", - "type": "backend", - "label": "Local coding agents", - "sublabel": "Propose changes", - "pos": [ - 20, - 260 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "gates", - "type": "security", - "label": "Repository + CI", - "sublabel": "Verify exact revision", - "pos": [ - 20, - 380 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "registry", - "type": "backend", - "label": "Governed skills", - "sublabel": "Pinned + qualified", - "pos": [ - 260, - 140 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "approval", - "type": "security", - "label": "Human approval", - "sublabel": "Exact artifact + scope", - "pos": [ - 260, - 380 - ], - "size": [ - 180, - 60 - ] - }, - { - "id": "release", - "type": "security", - "label": "Release service", - "sublabel": "Production credentials", - "pos": [ - 260, - 500 - ], - "size": [ - 180, - 60 - ] - } + {"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]} ], "connections": [ - { - "id": "flow-0", - "from": "people", - "to": "coordinator", - "label": "Authorize scope" - }, - { - "id": "flow-1", - "from": "coordinator", - "to": "clients", - "label": "Route tasks" - }, - { - "id": "flow-2", - "from": "registry", - "to": "coordinator", - "label": "Guide" - }, - { - "id": "flow-3", - "from": "registry", - "to": "clients", - "label": "Guide" - }, - { - "id": "flow-4", - "from": "clients", - "to": "gates", - "label": "Propose" - }, - { - "id": "flow-5", - "from": "gates", - "to": "approval", - "label": "Evidence" - }, - { - "id": "flow-6", - "from": "approval", - "to": "release", - "label": "Authorize" - } + {"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]} ] } \ No newline at end of file diff --git a/docs/strategy/index.mdx b/docs/strategy/index.mdx index 90393ab..96187fb 100644 --- a/docs/strategy/index.mdx +++ b/docs/strategy/index.mdx @@ -1,7 +1,7 @@ --- id: index title: AI adoption strategy -description: Govern skills first; outcomes before activity; explicit target status. +description: An autonomous SDLC cycle directed, guided and reviewed by humans, supported by governed skills. slug: / hide_table_of_contents: true --- @@ -12,15 +12,15 @@ import ArchifyDiagram from '@site/src/components/ArchifyDiagram'; **TO-BE · Target operating model — not installed controls or company results.** -## How do we reach dependable AI-assisted work? +## Autonomous SDLC, human direction -Start with governed skills, then widen coordination only when evidence supports it. The destination is one logical SDLC coordinator alongside people using qualified local coding agents. + - +An autonomous agent executes and governs the SDLC. Humans direct, guide and review the work throughout the cycle. -- **Skills first:** reusable working knowledge, not permission to act. -- **People stay accountable:** the six departments and eleven teams keep their decisions. -- **Outcomes before activity:** quality, risk and total cost determine whether to continue. +**Scope → Architecture → UI/UX → Planning → Delivery → UAT → 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.
Strategy rationale and reading map @@ -31,7 +31,7 @@ Start with governed skills, then widen coordination only when evidence supports Eldorado's target is to make AI-assisted work dependable across the organization: useful guidance, clear human ownership, controlled execution and measurable outcomes. The first investment is a governed skills system, not an autonomous replacement for the organization. A skill packages reusable working knowledge, prerequisites and verification steps. It helps an agent perform a task; it does not grant permission or guarantee that the work is correct. -The target combines one logical SDLC coordinating agent with developer choice on local laptops. Copilot, Claude Code and Cline remain client options within a qualified support matrix. They receive the same reviewed portable skill content through client-specific adapters. Repository, CI and release services remain the enforcement points, independently of what any agent says or which local tool produced a change. +The target combines an autonomous agent executing and governing the SDLC with humans directing, guiding and reviewing it. Developer choice on local laptops remains a supporting option: Copilot, Claude Code and Cline can receive reviewed portable skill content through qualified adapters. Repository, CI and release controls support execution; detailed approval policies remain implementation decisions, not new roles established by this strategy. The strategy describes the intended operating model and the evidence required to introduce it. It does not establish that these controls are installed, that every client is supported, or that AI has improved Eldorado's performance. The current reference is the documented six-department organization. Rollout begins by validating actual clients, responsibilities, data boundaries and available evidence. @@ -39,7 +39,7 @@ Success means accepted outcomes with maintained quality, controlled risk and und ## Navigate the strategy -- [Target operating model](./target-operating-model.mdx) — One logical coordinator; local client choice; independent authority; all departments. +- [Target operating model](./target-operating-model.mdx) — The autonomous SDLC cycle, with human direction, guidance and review. - [Skills governance](./skills-governance.mdx) — Contract, lifecycle, decision rights, adapters, locking, exceptions. - [Skills quality](./skills-quality.mdx) — Risk tiers, independent evaluation, repeated trials, adversarial conformance. - [Skills adoption](./skills-adoption.mdx) — Workflow-led coaching, client choice, feedback, manual path. @@ -49,6 +49,6 @@ Success means accepted outcomes with maintained quality, controlled risk and und
-**Next:** [See how people, skills and delivery controls connect →](./target-operating-model.mdx) +**Next:** [Explore the autonomous SDLC cycle →](./target-operating-model.mdx) diff --git a/docs/strategy/target-operating-model.mdx b/docs/strategy/target-operating-model.mdx index d41be87..b761e03 100644 --- a/docs/strategy/target-operating-model.mdx +++ b/docs/strategy/target-operating-model.mdx @@ -1,7 +1,7 @@ --- id: target-operating-model title: Target operating model -description: One logical coordinator; local client choice; independent authority; all departments. +description: An autonomous agent executes and governs the SDLC; humans direct, guide and review the repeating cycle. hide_table_of_contents: true --- @@ -9,42 +9,42 @@ import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
-**TO-BE · Target operating model — not installed controls or company results.** +**TO-BE · Target operating model — the intended strategy, not a deployed system.** -## Who coordinates, and who can authorize delivery? +## Autonomous SDLC, human direction -One logical coordinator routes work; people retain authority. Local agents propose changes, while independent CI and human approval govern what the release service can execute. + - +An autonomous agent executes and governs the SDLC. Humans direct, guide and review the work throughout the cycle. -- **Local choice:** Copilot, Claude Code and Cline use qualified adapters and pinned skills. -- **Central controls:** neither laptops nor the coordinator hold production credentials. -- **Existing organization:** six departments, eleven teams; Data & AI owns coordination, not their decisions. +**Scope → Architecture → UI/UX → Planning → Delivery → UAT → Releases → Scope** + +Each stage follows the previous one. Releases returns to Scope; the cycle repeats indefinitely, with human direction informing the next iteration.
-Trust boundaries, recovery and detailed delivery path +Supporting implementation options: trust boundaries and recovery -## Coordination without transferred authority +## Controls that support autonomous execution -The diagram is an overview; the equivalent delivery path below remains readable on narrow screens. Expand the diagram to inspect its trust boundaries and use its zoom controls for individual relationships. +The lifecycle above is the strategic direction. The earlier control design below is a supporting implementation option, not an agreed assignment of approval roles or a claim of installed controls. Detailed approval policy and responsibilities remain to be defined with humans. -1. Accountable people authorize the work scope; one logical coordinator routes tasks. +1. Humans direct, guide and review; the autonomous agent executes and governs work across the lifecycle. 2. The reviewed registry supplies pinned skills and qualified adapters to coordinator and local clients. -3. Laptop contributors propose changes; independent repository and CI checks verify the exact revision. -4. A human approves that artifact; only the release service holds production credentials. -5. Restricted audit and measurement evidence returns to people and informs the next authorized task. +3. Laptop contributors can participate alongside the agent; independent repository and CI checks verify the exact revision. +4. A separate release service and artifact-scoped approval are possible controls, subject to the policy humans define. +5. Restricted audit and measurement evidence supports human review and the next Scope iteration. - + **TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results. ### One coordination function, distributed contributions -Data & AI owns the coordinating function, with the existing AI Architect function connecting it to Technology leadership. The logical coordinator maintains approved work records, dependencies, evidence requests, bounded retries and escalation. It can summarize, propose work and route decisions. It cannot declare its own work approved, change protected checks, authorize production release or replace the responsible human. +The autonomous agent is more than a task router: it executes and governs the repeating SDLC under human direction, guidance and review. A coordinating function maintains work records, dependencies, evidence requests, bounded retries and escalation. Data & AI and the existing AI Architect function are possible implementation contributors, not newly assigned approval authorities. Autonomous execution does not remove human involvement or allow the agent to redefine its own controls. 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. -The delivery path is: an approved problem and acceptance criteria; an authorized task; local or isolated worker proposals; independently reviewed repository changes; exact-revision CI evidence; a human release decision; execution by the release service; then operational feedback. Product Management accepts the outcome hypothesis and business result. Product Design reviews experience and accessibility. Product Engineering owns the code and maintainability. Quality Engineering assesses quality independently. Architecture & Security assesses risk. Platform & IT operates the delivery boundary. No arrow from a laptop or coordinator goes directly to production authority. +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. ### Trust and authority boundaries @@ -57,10 +57,12 @@ Central controls verify protected-branch review, actual artifact identity, exact
-Responsibilities and first workflows for all eleven teams +Candidate contributions and first workflows for all eleven teams ### Department contributions +The existing team structure provides a starting point. The contributions below are implementation proposals, not settled approval roles or lifecycle gates. + ### Product / Product Management **Contribution and retained human decision** — Own pilot priorities, acceptance criteria and outcome hypotheses; decide product value. diff --git a/package.json b/package.json index 456a08a..b7a51f7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "verify:build": "node scripts/verify.mjs --build", "diagrams:build": "node scripts/build-diagrams.mjs", "test": "node --test scripts/*.test.mjs", - "test:browser": "node scripts/browser/strategy-story.cjs && node scripts/browser/inline-controls.cjs", + "test:browser": "node scripts/browser/strategy-story.cjs && node scripts/browser/inline-controls.cjs && node scripts/browser/sdlc-cycle.cjs", "kpi:example": "node scripts/ai-kpi.mjs" }, "dependencies": { diff --git a/scripts/browser/inline-controls.cjs b/scripts/browser/inline-controls.cjs index d70a804..6b6aa5f 100644 --- a/scripts/browser/inline-controls.cjs +++ b/scripts/browser/inline-controls.cjs @@ -121,6 +121,8 @@ const fs = require('node:fs'); 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(); + 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'}`); await next.scrollIntoViewIfNeeded(); await (await (await next.elementHandle()).contentFrame()).waitForFunction(() => document.documentElement.dataset.theme === 'dark'); diff --git a/scripts/browser/sdlc-cycle.cjs b/scripts/browser/sdlc-cycle.cjs new file mode 100644 index 0000000..6faf892 --- /dev/null +++ b/scripts/browser/sdlc-cycle.cjs @@ -0,0 +1,53 @@ +const {chromium} = require('playwright'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +(async () => { + const base = process.argv[2] || 'http://127.0.0.1:4173'; + const output = `.cache/cycle-${process.argv[3] || 'local'}`; + fs.mkdirSync(output, {recursive:true}); + const browser = await chromium.launch({headless:true, executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, args:['--no-sandbox']}); + const results = []; + try { + for (const width of [1440, 390, 320]) { + const context = await browser.newContext({viewport:{width,height:1000}, colorScheme:'light'}); + const page = await context.newPage(); + await page.goto(`${base}/eldorado-v1/documentation/`, {waitUntil:'networkidle'}); + if (width < 700) await page.getByRole('button', {name:'Toggle navigation bar'}).click(); + const nav = page.getByRole('link', {name:'Strategy', exact:true}).filter({visible:true}); + assert((await nav.getAttribute('href')).endsWith('/strategy/target-operating-model/')); + await nav.click(); + 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'); + const iframe = page.locator(`.archify-panel--story > .story-${width > 700 ? 'desktop' : 'mobile'}`); + await iframe.scrollIntoViewIfNeeded(); + const frame = await (await iframe.elementHandle()).contentFrame(); + await frame.locator('svg[role="img"]').waitFor(); + const metrics = await frame.evaluate(() => { + const svg = document.querySelector('svg[role="img"]'); + const labels = [...svg.querySelectorAll('text[data-node-label]')].map(n => ({text:n.textContent,px:parseFloat(getComputedStyle(n).fontSize)*n.getScreenCTM().a})); + const edges = [...svg.querySelectorAll('path[data-edge-id^="stage-"]')].map(n => [n.dataset.edgeFrom,n.dataset.edgeTo,n.getAttribute('marker-end')]); + const overflow = [...svg.querySelectorAll('[data-node-id]')].flatMap(n => { + 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 ids = ['scope','architecture','ui-ux','planning','delivery','uat','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(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.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)); + } + await context.close(); + } + assert.equal(results.length,6); + console.log('PASS 6 cycle routes/viewports: navigation, sidebar order, exact directed loop, arrowheads, >=14px labels, text bounds and no overflow'); + } finally {await browser.close();} +})().catch(error=>{console.error(error);process.exitCode=1;}); diff --git a/scripts/browser/strategy-story.cjs b/scripts/browser/strategy-story.cjs index e274855..3b64422 100644 --- a/scripts/browser/strategy-story.cjs +++ b/scripts/browser/strategy-story.cjs @@ -19,7 +19,7 @@ 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<=7,id); + assert(geometry.nodes.length>=5 && geometry.nodes.length<=(['index','target-operating-model'].includes(id)?9: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); diff --git a/scripts/build-diagrams.mjs b/scripts/build-diagrams.mjs index d64a792..b27a332 100644 --- a/scripts/build-diagrams.mjs +++ b/scripts/build-diagrams.mjs @@ -4,6 +4,7 @@ import {execFileSync} from 'node:child_process'; const cli = 'vendor/archify/bin/archify.mjs'; const types = new Set(['architecture','workflow','sequence','dataflow','lifecycle']); const inlineStyles = readFileSync(new URL('./inline-viewer.css', import.meta.url), 'utf8'); +const cycleStyles = readFileSync(new URL('./cycle-viewer.css', import.meta.url), 'utf8'); const themeBridge = readFileSync(new URL('./viewer-theme-bridge.js', import.meta.url), 'utf8'); // This directory is generated-only and ignored by Git. Remove stale artifacts. rmSync('static/diagrams', {recursive: true, force: true}); @@ -36,7 +37,8 @@ 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('')) throw new Error(`Invalid viewer: ${output}`); - writeFileSync(output, viewer.replace('', ``) + const storyStyles = ['strategy-target', 'strategy-target-mobile'].includes(name) ? cycleStyles : ''; + writeFileSync(output, viewer.replace('', ``) .replace('', ``)); renameSync(`static/diagrams/${name}/index.delivery.json`, `.cache/archify/${name}.delivery.json`); } diff --git a/scripts/cycle-viewer.css b/scripts/cycle-viewer.css new file mode 100644 index 0000000..6bff0ac --- /dev/null +++ b/scripts/cycle-viewer.css @@ -0,0 +1,14 @@ +/* Readable lifecycle story at inline scale, isolated from other diagrams. */ +.diagram-container > svg text { + font-family: Arial, sans-serif; + letter-spacing: 0; + font-size: 18px; +} +.diagram-container > svg text[data-node-label] { + font-size: 24px; + transform: translateY(-3px); +} +.diagram-container > svg text[data-detail="context"] { + font-size: 18px; + transform: translateY(7px); +} diff --git a/scripts/sdlc-cycle.test.mjs b/scripts/sdlc-cycle.test.mjs new file mode 100644 index 0000000..4b0703c --- /dev/null +++ b/scripts/sdlc-cycle.test.mjs @@ -0,0 +1,36 @@ +import {test} from 'node:test'; +import assert from 'node:assert/strict'; +import {readFileSync} from 'node:fs'; +const read = p => readFileSync(p, 'utf8'); +const stages = ['Scope', 'Architecture', 'UI/UX', 'Planning', 'Delivery', 'UAT', 'Releases']; + +test('Strategy opens at the autonomous lifecycle, with a matching direct landing story', () => { + assert.match(read('sidebars/strategy.ts'), /strategySidebar: \['target-operating-model', 'index'/); + for (const id of ['index', 'target-operating-model']) { + const page = read(`docs/strategy/${id}.mdx`); + const primary = page.split('
')[0]; + assert(primary.includes('name="strategy-target"')); + assert(primary.includes('autonomous agent')); + assert(primary.includes('direct, guide and review')); + assert(primary.includes('Scope → Architecture → UI/UX → Planning → Delivery → UAT → Releases → Scope')); + assert(primary.includes('TO-BE')); + assert(!/only routes|People authorize|independent gates protect release/.test(primary)); + } +}); + +test('both target diagrams have exactly seven sequential stages and a Releases to Scope return', () => { + for (const suffix of ['', '-mobile']) { + const d = JSON.parse(read(`diagrams/strategy-target${suffix}.architecture.json`)); + const ids = stages.map(label => d.components.find(n => n.label === label)?.id); + assert(ids.every(Boolean)); + assert.equal(new Set(ids).size, 7); + const flow = d.connections.filter(e => ids.includes(e.from) && ids.includes(e.to)); + assert.deepEqual(flow.map(e => [e.from, e.to]), ids.map((id, i) => [id, ids[(i + 1) % ids.length]])); + assert.match(flow.at(-1).label, /Repeat/); + assert(flow.at(-1).via.length >= 2, 'explicit outside return corridor'); + assert(d.components.some(n => n.label === 'Autonomous agent' && /Executes.*governs/.test(n.sublabel))); + assert(d.components.some(n => n.label === 'Humans' && n.sublabel === 'Direct, guide and review')); + assert(d.connections.some(e => e.from === 'humans' && e.to === 'agent')); + assert(d.connections.some(e => e.from === 'agent' && e.to === ids[0])); + } +}); diff --git a/scripts/strategy-story.test.mjs b/scripts/strategy-story.test.mjs index de5ffb3..ad92e1b 100644 --- a/scripts/strategy-story.test.mjs +++ b/scripts/strategy-story.test.mjs @@ -28,8 +28,8 @@ test('mobile story diagrams preserve the same nodes and directed relationships', const name = /]+ story/.exec(page)[1]; const desktop = JSON.parse(read(`diagrams/${name}.architecture.json`)); const mobile = JSON.parse(read(`diagrams/${name}-mobile.architecture.json`)); - assert(desktop.components.length >= 5 && desktop.components.length <= 7, name); - const semantic = d => ({nodes:d.components.map(({pos,size,...n})=>n),edges:d.connections.map(({fromSide,toSide,...e})=>e)}); + assert(desktop.components.length >= 5 && desktop.components.length <= (name === 'strategy-target' ? 9 : 7), name); + const semantic = d => ({nodes:d.components.map(({pos,size,...n})=>n),edges:d.connections.map(({fromSide,toSide,via,labelAt,...e})=>e)}); assert.deepEqual(semantic(desktop), semantic(mobile), name); assert(desktop.connections.every(e => e.label && e.from !== e.to), name); assert.equal(desktop.meta.legend.mode, 'hidden'); diff --git a/sidebars/strategy.ts b/sidebars/strategy.ts index 321f4cd..eb26393 100644 --- a/sidebars/strategy.ts +++ b/sidebars/strategy.ts @@ -1,2 +1,2 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; -export default {strategySidebar: ['index', 'target-operating-model', 'skills-governance', 'skills-quality', 'skills-adoption', 'measurement', 'skills-maintenance', 'roadmap']} satisfies SidebarsConfig; +export default {strategySidebar: ['target-operating-model', 'index', 'skills-governance', 'skills-quality', 'skills-adoption', 'measurement', 'skills-maintenance', 'roadmap']} satisfies SidebarsConfig; diff --git a/src/css/custom.css b/src/css/custom.css index 46f1624..3e3a781 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -110,7 +110,7 @@ a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offse .archify-panel--story {max-width: 740px;} .archify-panel--story > iframe {height: auto; aspect-ratio: 480 / 408;} .archify-panel--story > .story-mobile {display: none;} -.archify-panel--story[data-diagram="strategy-target"] > iframe {aspect-ratio: 480 / 628;} +.archify-panel--story[data-diagram="strategy-target"] > iframe {aspect-ratio: 960 / 708;} .archify-panel--story[data-diagram="strategy-adoption"] > iframe {aspect-ratio: 545 / 408;} .archify-panel--story[data-diagram="strategy-maintenance"] > iframe {aspect-ratio: 524 / 408;} .strategy-content > details {margin: .65rem 0; border: 1px solid var(--line); padding: .8rem 1rem; background: var(--surface);} @@ -124,7 +124,7 @@ a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offse .archify-panel--story[data-diagram="strategy-roadmap"] > .story-mobile {aspect-ratio: 240 / 628;} .archify-panel--story[data-diagram="strategy-adoption"] > .story-mobile {aspect-ratio: 305 / 748;} .archify-panel--story[data-diagram="strategy-maintenance"] > .story-mobile {aspect-ratio: 284 / 748;} - .archify-panel--story[data-diagram="strategy-target"] > .story-mobile {aspect-ratio: 301 / 868;} + .archify-panel--story[data-diagram="strategy-target"] > .story-mobile {aspect-ratio: 340 / 1520;} } .archify-expand {display: grid; place-items: center; width: 44px; height: 44px; border: 0; padding: 0; background: transparent; color: var(--ifm-link-color); cursor: pointer;} .archify-expand:hover {background: var(--ifm-hover-overlay);}