Compare commits

..
Author SHA1 Message Date
jarvis-at-skic a35cade49c feat: [verified] compose Skills Adoption diagrams around their stories
Build and publish Eldorado documentation / build (pull_request) Successful in 40s
2026-10-06 22:35:48 +00:00
jarvis-at-skic ef169cec58 Merge pull request 'fix: expose every documentation page in navigation' (#32) from fix/all-docs-sidebar into main
Build and publish Eldorado documentation / build (push) Successful in 41s
2026-10-06 13:15:15 -07:00
jarvis-at-skic e0afa78eb2 test: [verified] include Reference in measurement navigation
Build and publish Eldorado documentation / build (pull_request) Successful in 43s
2026-10-06 20:14:19 +00:00
jarvis-at-skic e087adab1e fix: [verified] expose all documentation in reachable sidebars
Build and publish Eldorado documentation / build (pull_request) Successful in 47s
2026-10-06 20:00:08 +00:00
jarvis-at-skic 325363d89c Merge pull request 'feat: wider, richer Skills Adoption diagrams' (#31) from feat/skills-adoption-stories into main
Build and publish Eldorado documentation / build (push) Successful in 44s
2026-10-06 12:41:28 -07:00
jarvis-at-skic ad7312b65a feat: [verified] widen Skills Adoption diagram stories
Build and publish Eldorado documentation / build (pull_request) Successful in 39s
2026-10-06 19:37:49 +00:00
jarvis-at-skic 3238e1d809 Merge pull request 'docs: group Strategy into Skills Adoption and Autonomous Agent stages' (#30) from feat/strategy-stage-navigation into main
Build and publish Eldorado documentation / build (push) Successful in 42s
2026-10-06 12:07:57 -07:00
jarvis-at-skic fe4c3509d1 docs: [verified] group Strategy into adoption and autonomy stages
Build and publish Eldorado documentation / build (pull_request) Successful in 40s
2026-10-06 19:07:00 +00:00
jarvis-at-skic 8a7dbea852 Merge pull request 'docs: connect skills-first Roadmap to autonomous Target' (#29) from feat/roadmap-skills-to-target into main
Build and publish Eldorado documentation / build (push) Successful in 39s
2026-10-06 04:36:20 -07:00
41 changed files with 3018 additions and 848 deletions

No files matched your search

@@ -2,138 +2,244 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Learn before expanding",
"title": "TO-BE: Adoption",
"output": "static/diagrams/strategy-adoption-mobile/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
880,
2040
]
},
"components": [
{
"id": "choose",
"type": "backend",
"label": "Choose workflow",
"sublabel": "Department outcome",
"id": "delivery",
"type": "external",
"label": "Delivery first",
"tag": "Start with a real workflow",
"sublabel": "Baseline + bounded canary",
"pos": [
20,
20
60,
120
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "coach",
"id": "human",
"type": "backend",
"label": "Coach people",
"sublabel": "Verify + recover",
"label": "Human",
"tag": "Direct + guide + review",
"sublabel": "Define outcome; inspect artifacts",
"pos": [
20,
140
60,
1280
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "canary",
"type": "backend",
"label": "Run bounded canary",
"sublabel": "Qualified skill + client",
"id": "agent",
"type": "external",
"label": "AI Agent",
"tag": "Supported client + approved skills",
"sublabel": "Produce work within the boundary",
"pos": [
20,
260
60,
910
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "accept",
"type": "backend",
"label": "Accept real work",
"sublabel": "Independent review",
"id": "learn",
"type": "external",
"label": "Learn from real work",
"tag": "Ongoing, reviewed improvement",
"sublabel": "Feedback + friction + measures\nRetain a manual path\nNo quotas or rankings",
"pos": [
20,
380
60,
490
],
"size": [
180,
60
]
},
{
"id": "feedback",
"type": "backend",
"label": "Review feedback",
"sublabel": "Usefulness + non-use",
"pos": [
20,
500
740,
270
],
"size": [
180,
60
]
"icon": "none"
},
{
"id": "expand",
"type": "backend",
"label": "Expand or revise",
"sublabel": "Keep a manual path",
"label": "Expand on evidence",
"tag": "Other SDLC phases",
"sublabel": "Later order undecided\nDelivery keeps improving",
"pos": [
20,
620
60,
1650
],
"size": [
180,
60
]
740,
240
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "choose",
"to": "coach",
"label": "Prepare"
},
{
"id": "flow-1",
"from": "coach",
"to": "canary",
"label": "support pilot"
},
{
"id": "flow-2",
"from": "canary",
"to": "accept",
"label": "Verify"
},
{
"id": "flow-3",
"from": "accept",
"to": "feedback",
"label": "learn"
},
{
"id": "flow-4",
"from": "feedback",
"to": "expand",
"label": "decide"
},
{
"id": "feedback-loop",
"from": "expand",
"to": "coach",
"label": "Review revision",
"variant": "dashed",
"id": "delivery-agent",
"from": "delivery",
"to": "agent",
"label": "Practice",
"fromSide": "right",
"toSide": "right"
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
230.0
],
[
850,
1020.0
]
],
"labelAt": [
780,
870
]
},
{
"id": "human-agent",
"from": "human",
"to": "agent",
"label": "Direction",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
824,
1390.0
],
[
824,
1020.0
]
],
"labelAt": [
770,
1240
]
},
{
"id": "agent-human",
"from": "agent",
"to": "human",
"label": "Work to inspect",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1185
]
},
{
"id": "agent-learn",
"from": "agent",
"to": "learn",
"label": "Observe",
"fromSide": "top",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
815
]
},
{
"id": "learn-delivery",
"from": "learn",
"to": "delivery",
"label": "Improve",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
42,
625.0
],
[
42,
230.0
]
],
"labelAt": [
620,
80
]
},
{
"id": "human-expand",
"from": "human",
"to": "expand",
"label": "Accepted work",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1555
]
},
{
"id": "learn-expand",
"from": "learn",
"to": "expand",
"label": "Feedback",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
20,
625.0
],
[
20,
1770.0
]
],
"labelAt": [
620,
1610
]
}
]
}
}
+198 -94
View File
@@ -2,138 +2,242 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Learn before expanding",
"title": "TO-BE: Adoption",
"output": "static/diagrams/strategy-adoption/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
2040,
1160
]
},
"components": [
{
"id": "choose",
"type": "backend",
"label": "Choose workflow",
"sublabel": "Department outcome",
"id": "delivery",
"type": "external",
"label": "Delivery first",
"tag": "Start with a real workflow",
"sublabel": "Baseline + bounded canary",
"pos": [
20,
20
60,
350
],
"size": [
180,
60
]
470,
210
],
"icon": "none"
},
{
"id": "coach",
"id": "human",
"type": "backend",
"label": "Coach people",
"sublabel": "Verify + recover",
"label": "Human",
"tag": "Direct + guide + review",
"sublabel": "Define outcome; inspect artifacts",
"pos": [
260,
20
700,
90
],
"size": [
180,
60
]
570,
190
],
"icon": "none"
},
{
"id": "canary",
"type": "backend",
"label": "Run bounded canary",
"sublabel": "Qualified skill + client",
"id": "agent",
"type": "external",
"label": "AI Agent",
"tag": "Supported client + approved skills",
"sublabel": "Produce work within the boundary",
"pos": [
260,
150
700,
440
],
"size": [
180,
60
]
570,
190
],
"icon": "none"
},
{
"id": "accept",
"type": "backend",
"label": "Accept real work",
"sublabel": "Independent review",
"id": "learn",
"type": "external",
"label": "Learn from real work",
"tag": "Ongoing, reviewed improvement",
"sublabel": "Feedback + friction + measures\nRetain a manual path\nNo quotas or rankings",
"pos": [
20,
150
700,
800
],
"size": [
180,
60
]
},
{
"id": "feedback",
"type": "backend",
"label": "Review feedback",
"sublabel": "Usefulness + non-use",
"pos": [
20,
280
570,
270
],
"size": [
180,
60
]
"icon": "none"
},
{
"id": "expand",
"type": "backend",
"label": "Expand or revise",
"sublabel": "Keep a manual path",
"label": "Expand on evidence",
"tag": "Other SDLC phases",
"sublabel": "Later order undecided\nDelivery keeps improving",
"pos": [
260,
280
1460,
360
],
"size": [
180,
60
]
520,
240
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "choose",
"to": "coach",
"label": "Prepare"
},
{
"id": "flow-1",
"from": "coach",
"to": "canary",
"label": "support pilot"
},
{
"id": "flow-2",
"from": "canary",
"to": "accept",
"label": "Verify"
},
{
"id": "flow-3",
"from": "accept",
"to": "feedback",
"label": "learn"
},
{
"id": "flow-4",
"from": "feedback",
"to": "expand",
"label": "decide"
},
{
"id": "feedback-loop",
"from": "expand",
"to": "coach",
"label": "Review revision",
"variant": "dashed",
"id": "delivery-agent",
"from": "delivery",
"to": "agent",
"label": "Practice",
"fromSide": "right",
"toSide": "right"
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
585,
455
],
[
585,
535
]
],
"labelAt": [
570,
600
]
},
{
"id": "human-agent",
"from": "human",
"to": "agent",
"label": "Direction",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
1100,
360
]
},
{
"id": "agent-human",
"from": "agent",
"to": "human",
"label": "Work to inspect",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
630,
535
],
[
630,
185
]
],
"labelAt": [
470,
240
]
},
{
"id": "agent-learn",
"from": "agent",
"to": "learn",
"label": "Observe",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
1100,
720
]
},
{
"id": "learn-delivery",
"from": "learn",
"to": "delivery",
"label": "Improve",
"fromSide": "left",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
295,
935
]
],
"labelAt": [
450,
900
]
},
{
"id": "human-expand",
"from": "human",
"to": "expand",
"label": "Accepted work",
"fromSide": "right",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1720,
185
]
],
"labelAt": [
1550,
140
]
},
{
"id": "learn-expand",
"from": "learn",
"to": "expand",
"label": "Feedback",
"fromSide": "right",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1720,
935
]
],
"labelAt": [
1480,
895
]
}
]
}
}
@@ -2,129 +2,246 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Independent approval",
"title": "TO-BE: Governance",
"output": "static/diagrams/strategy-governance-mobile/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
880,
2710
]
},
"components": [
{
"id": "owner",
"type": "security",
"label": "Owner proposes",
"sublabel": "Existing accountable team",
"type": "external",
"label": "01 Propose",
"tag": "Owning team",
"sublabel": "Scope + testable outcome",
"pos": [
20,
20
60,
120
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "review",
"type": "security",
"label": "Independent review",
"sublabel": "Quality + risk evidence",
"type": "external",
"label": "02 Review independently",
"tag": "Quality + Security",
"sublabel": "Author is not sole approver",
"pos": [
20,
140
60,
490
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "canary",
"type": "security",
"label": "Bounded canary",
"sublabel": "Qualified client path",
"type": "external",
"label": "03 Qualify real use",
"tag": "Human + AI Agent",
"sublabel": "Exact client + bounded canary",
"pos": [
20,
260
60,
860
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "approve",
"type": "security",
"label": "Approve revision",
"sublabel": "Exact digest + scope",
"type": "backend",
"label": "04 Approve exact scope",
"tag": "Accountable approvers",
"sublabel": "Digest + permitted uses + expiry",
"pos": [
20,
380
60,
1230
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "publish",
"type": "security",
"label": "Publish bundle",
"sublabel": "Data & AI catalog",
"type": "backend",
"label": "05 Publish the bundle",
"tag": "Data & AI catalog",
"sublabel": "Pin skill, adapter + dependencies",
"pos": [
20,
500
60,
1600
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "distribute",
"type": "security",
"label": "Verify effective state",
"sublabel": "Platform & IT",
"type": "backend",
"label": "06 Verify loaded state",
"tag": "Platform & IT",
"sublabel": "Read revision; verify reload",
"pos": [
20,
620
60,
1970
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "withdraw",
"type": "security",
"label": "Withdraw / roll back",
"tag": "Unsafe revision",
"sublabel": "Revoke; restore last-known-good",
"pos": [
60,
2340
],
"size": [
740,
220
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"id": "owner-review",
"from": "owner",
"to": "review",
"label": "submit"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
395
]
},
{
"id": "flow-1",
"id": "review-canary",
"from": "review",
"to": "canary",
"label": "qualify"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
765
]
},
{
"id": "flow-2",
"id": "canary-approve",
"from": "canary",
"to": "approve",
"label": "Accept"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1135
]
},
{
"id": "flow-3",
"id": "approve-publish",
"from": "approve",
"to": "publish",
"label": "record approval"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1505
]
},
{
"id": "flow-4",
"id": "publish-distribute",
"from": "publish",
"to": "distribute",
"label": "Deliver"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1875
]
},
{
"id": "approve-owner",
"from": "approve",
"to": "owner",
"label": "Rework",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
20,
1340.0
],
[
20,
230.0
]
],
"labelAt": [
620,
80
]
},
{
"id": "distribute-withdraw",
"from": "distribute",
"to": "withdraw",
"label": "Unsafe",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
610,
2245
]
}
]
}
}
+208 -61
View File
@@ -2,129 +2,276 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Independent approval",
"title": "TO-BE: Governance",
"output": "static/diagrams/strategy-governance/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
1760,
1420
]
},
"components": [
{
"id": "owner",
"type": "security",
"label": "Owner proposes",
"sublabel": "Existing accountable team",
"type": "external",
"label": "01 Propose",
"tag": "Owning team",
"sublabel": "Scope + testable outcome",
"pos": [
20,
20
80,
60
],
"size": [
180,
60
]
500,
170
],
"icon": "none"
},
{
"id": "review",
"type": "security",
"label": "Independent review",
"sublabel": "Quality + risk evidence",
"type": "external",
"label": "02 Review independently",
"tag": "Quality + Security",
"sublabel": "Author is not sole approver",
"pos": [
260,
20
300,
280
],
"size": [
180,
60
]
500,
170
],
"icon": "none"
},
{
"id": "canary",
"type": "security",
"label": "Bounded canary",
"sublabel": "Qualified client path",
"type": "external",
"label": "03 Qualify real use",
"tag": "Human + AI Agent",
"sublabel": "Exact client + bounded canary",
"pos": [
260,
150
520,
500
],
"size": [
180,
60
]
500,
170
],
"icon": "none"
},
{
"id": "approve",
"type": "security",
"label": "Approve revision",
"sublabel": "Exact digest + scope",
"type": "backend",
"label": "04 Approve exact scope",
"tag": "Accountable approvers",
"sublabel": "Digest + permitted uses + expiry",
"pos": [
20,
150
740,
720
],
"size": [
180,
60
]
500,
170
],
"icon": "none"
},
{
"id": "publish",
"type": "security",
"label": "Publish bundle",
"sublabel": "Data & AI catalog",
"type": "backend",
"label": "05 Publish the bundle",
"tag": "Data & AI catalog",
"sublabel": "Pin skill, adapter + dependencies",
"pos": [
20,
280
960,
940
],
"size": [
180,
60
]
500,
170
],
"icon": "none"
},
{
"id": "distribute",
"type": "security",
"label": "Verify effective state",
"sublabel": "Platform & IT",
"type": "backend",
"label": "06 Verify loaded state",
"tag": "Platform & IT",
"sublabel": "Read revision; verify reload",
"pos": [
260,
280
1180,
1160
],
"size": [
180,
60
]
500,
170
],
"icon": "none"
},
{
"id": "withdraw",
"type": "security",
"label": "Withdraw / roll back",
"tag": "Unsafe revision",
"sublabel": "Revoke; restore last-known-good",
"pos": [
80,
1160
],
"size": [
660,
170
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"id": "owner-review",
"from": "owner",
"to": "review",
"label": "submit"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
330,
255
],
[
550,
255
]
]
},
{
"id": "flow-1",
"id": "review-canary",
"from": "review",
"to": "canary",
"label": "qualify"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
550,
475
],
[
770,
475
]
]
},
{
"id": "flow-2",
"id": "canary-approve",
"from": "canary",
"to": "approve",
"label": "Accept"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
770,
695
],
[
990,
695
]
]
},
{
"id": "flow-3",
"id": "approve-publish",
"from": "approve",
"to": "publish",
"label": "record approval"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
990,
915
],
[
1210,
915
]
]
},
{
"id": "flow-4",
"id": "publish-distribute",
"from": "publish",
"to": "distribute",
"label": "Deliver"
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1210,
1135
],
[
1430,
1135
]
]
},
{
"id": "approve-owner",
"from": "approve",
"to": "owner",
"label": "Rework",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
40,
805
],
[
40,
145
]
],
"labelAt": [
180,
680
]
},
{
"id": "distribute-withdraw",
"from": "distribute",
"to": "withdraw",
"label": "Unsafe",
"fromSide": "left",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
950,
1220
]
}
]
}
}
@@ -2,140 +2,337 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Maintain trust over time",
"title": "TO-BE: Maintenance",
"output": "static/diagrams/strategy-maintenance-mobile/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
880,
3080
]
},
"components": [
{
"id": "monitor",
"type": "backend",
"label": "Monitor skills",
"sublabel": "Usefulness + safety",
"id": "observe",
"type": "external",
"label": "Observe use",
"tag": "Owner + Data & AI",
"sublabel": "Feedback, drift + review due",
"pos": [
20,
20
60,
120
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "triage",
"type": "backend",
"label": "Triage changes",
"sublabel": "Owner + risk review",
"type": "external",
"label": "Triage the signal",
"tag": "Owning team",
"sublabel": "Useful? Safe? Still needed?",
"pos": [
20,
140
60,
1970
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "correct",
"type": "backend",
"label": "Correct and test",
"sublabel": "Independent regression",
"id": "change",
"type": "external",
"label": "Change the skill",
"tag": "Reviewed correction",
"sublabel": "New immutable revision",
"pos": [
20,
260
60,
1230
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "decision",
"type": "backend",
"label": "Approve or hold",
"sublabel": "Exact revised bundle",
"id": "validate",
"type": "external",
"label": "Revalidate",
"tag": "Independent reviewers",
"sublabel": "Regression + client evidence",
"pos": [
20,
380
60,
860
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "promote",
"id": "release",
"type": "backend",
"label": "Promote safely",
"sublabel": "Verify state + monitor",
"label": "Approve + version",
"tag": "Accountable human authority",
"sublabel": "Verify distribution and recovery",
"pos": [
20,
500
60,
490
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "observe",
"id": "safe",
"type": "backend",
"label": "Quarantine / rollback",
"sublabel": "Revoke unsafe revision",
"label": "Continue safe use",
"tag": "Substantive review passes",
"sublabel": "Keep owner + next review date",
"pos": [
20,
620
60,
2710
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "retire",
"type": "external",
"label": "Retire deliberately",
"tag": "No longer useful",
"sublabel": "Reconcile consumers; no new use",
"pos": [
60,
2340
],
"size": [
740,
220
],
"icon": "none"
},
{
"id": "contain",
"type": "security",
"label": "Contain unsafe use",
"tag": "Security + Platform & IT",
"sublabel": "Revoke + quarantine + roll back\nNo automatic reinstatement",
"pos": [
60,
1600
],
"size": [
740,
220
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "monitor",
"to": "triage",
"label": "Detect"
},
{
"id": "flow-1",
"from": "triage",
"to": "correct",
"label": "scope correction"
},
{
"id": "flow-2",
"from": "correct",
"to": "decision",
"label": "Prove"
},
{
"id": "flow-3",
"from": "decision",
"to": "promote",
"label": "authorize"
},
{
"id": "unsafe",
"from": "triage",
"to": "observe",
"label": "Unsafe",
"variant": "security",
"fromSide": "right",
"toSide": "right"
},
{
"id": "review-loop",
"id": "observe-triage",
"from": "observe",
"to": "correct",
"label": "Requalify",
"variant": "dashed"
"to": "triage",
"label": "",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
20,
230.0
],
[
20,
2080.0
]
],
"labelAt": [
620,
1930
]
},
{
"id": "triage-change",
"from": "triage",
"to": "change",
"label": "Correct",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
2080.0
],
[
850,
1340.0
]
],
"labelAt": [
780,
1560
]
},
{
"id": "change-validate",
"from": "change",
"to": "validate",
"label": "",
"fromSide": "top",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1135
]
},
{
"id": "validate-release",
"from": "validate",
"to": "release",
"label": "",
"fromSide": "top",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
765
]
},
{
"id": "release-observe",
"from": "release",
"to": "observe",
"label": "",
"fromSide": "top",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
395
]
},
{
"id": "triage-safe",
"from": "triage",
"to": "safe",
"label": "Safe",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
824,
2080.0
],
[
824,
2820.0
]
],
"labelAt": [
620,
2670
]
},
{
"id": "triage-retire",
"from": "triage",
"to": "retire",
"label": "Retire",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
2245
]
},
{
"id": "triage-contain",
"from": "triage",
"to": "contain",
"label": "Unsafe",
"fromSide": "top",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
610,
1875
]
},
{
"id": "contain-change",
"from": "contain",
"to": "change",
"label": "Reviewed fix",
"fromSide": "top",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
610,
1505
]
},
{
"id": "safe-observe",
"from": "safe",
"to": "observe",
"label": "Keep watching",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
42,
2820.0
],
[
42,
230.0
]
],
"labelAt": [
620,
80
]
}
]
}
}
+321 -92
View File
@@ -2,140 +2,369 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Maintain trust over time",
"title": "TO-BE: Maintenance",
"output": "static/diagrams/strategy-maintenance/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
2630,
1300
]
},
"components": [
{
"id": "monitor",
"type": "backend",
"label": "Monitor skills",
"sublabel": "Usefulness + safety",
"id": "observe",
"type": "external",
"label": "Observe use",
"tag": "Owner + Data & AI",
"sublabel": "Feedback, drift + review due",
"pos": [
20,
20
670,
60
],
"size": [
180,
60
]
560,
170
],
"icon": "none"
},
{
"id": "triage",
"type": "backend",
"label": "Triage changes",
"sublabel": "Owner + risk review",
"type": "external",
"label": "Triage the signal",
"tag": "Owning team",
"sublabel": "Useful? Safe? Still needed?",
"pos": [
260,
20
1340,
330
],
"size": [
180,
60
]
500,
190
],
"icon": "none"
},
{
"id": "correct",
"type": "backend",
"label": "Correct and test",
"sublabel": "Independent regression",
"id": "change",
"type": "external",
"label": "Change the skill",
"tag": "Reviewed correction",
"sublabel": "New immutable revision",
"pos": [
260,
150
1240,
750
],
"size": [
180,
60
]
520,
180
],
"icon": "none"
},
{
"id": "decision",
"type": "backend",
"label": "Approve or hold",
"sublabel": "Exact revised bundle",
"id": "validate",
"type": "external",
"label": "Revalidate",
"tag": "Independent reviewers",
"sublabel": "Regression + client evidence",
"pos": [
20,
150
560,
970
],
"size": [
180,
60
]
560,
180
],
"icon": "none"
},
{
"id": "promote",
"id": "release",
"type": "backend",
"label": "Promote safely",
"sublabel": "Verify state + monitor",
"label": "Approve + version",
"tag": "Accountable human authority",
"sublabel": "Verify distribution and recovery",
"pos": [
20,
280
70,
570
],
"size": [
180,
60
]
540,
180
],
"icon": "none"
},
{
"id": "observe",
"id": "safe",
"type": "backend",
"label": "Quarantine / rollback",
"sublabel": "Revoke unsafe revision",
"label": "Continue safe use",
"tag": "Substantive review passes",
"sublabel": "Keep owner + next review date",
"pos": [
260,
280
2010,
80
],
"size": [
180,
60
]
550,
180
],
"icon": "none"
},
{
"id": "retire",
"type": "external",
"label": "Retire deliberately",
"tag": "No longer useful",
"sublabel": "Reconcile consumers; no new use",
"pos": [
2010,
430
],
"size": [
550,
180
],
"icon": "none"
},
{
"id": "contain",
"type": "security",
"label": "Contain unsafe use",
"tag": "Security + Platform & IT",
"sublabel": "Revoke + quarantine + roll back\nNo automatic reinstatement",
"pos": [
2010,
800
],
"size": [
550,
220
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "monitor",
"to": "triage",
"label": "Detect"
},
{
"id": "flow-1",
"from": "triage",
"to": "correct",
"label": "scope correction"
},
{
"id": "flow-2",
"from": "correct",
"to": "decision",
"label": "Prove"
},
{
"id": "flow-3",
"from": "decision",
"to": "promote",
"label": "authorize"
},
{
"id": "unsafe",
"from": "triage",
"to": "observe",
"label": "Unsafe",
"variant": "security",
"fromSide": "right",
"toSide": "right"
},
{
"id": "review-loop",
"id": "observe-triage",
"from": "observe",
"to": "correct",
"label": "Requalify",
"variant": "dashed"
"to": "triage",
"label": "",
"fromSide": "right",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1590,
145
]
]
},
{
"id": "triage-change",
"from": "triage",
"to": "change",
"label": "Correct",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1590,
620
],
[
1500,
620
]
],
"labelAt": [
1450,
680
]
},
{
"id": "change-validate",
"from": "change",
"to": "validate",
"label": "",
"fromSide": "bottom",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1500,
1060
]
]
},
{
"id": "validate-release",
"from": "validate",
"to": "release",
"label": "",
"fromSide": "left",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
340,
1060
]
]
},
{
"id": "release-observe",
"from": "release",
"to": "observe",
"label": "",
"fromSide": "top",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
340,
145
]
]
},
{
"id": "triage-safe",
"from": "triage",
"to": "safe",
"label": "Safe",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1920,
425
],
[
1920,
170
]
],
"labelAt": [
1930,
210
]
},
{
"id": "triage-retire",
"from": "triage",
"to": "retire",
"label": "Retire",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1950,
425
],
[
1950,
520
]
],
"labelAt": [
1900,
490
]
},
{
"id": "triage-contain",
"from": "triage",
"to": "contain",
"label": "Unsafe",
"fromSide": "bottom",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
1590,
580
],
[
1850,
580
],
[
1850,
910
]
],
"labelAt": [
1900,
720
]
},
{
"id": "contain-change",
"from": "contain",
"to": "change",
"label": "Reviewed fix",
"fromSide": "bottom",
"toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
2285,
1230
],
[
1500,
1230
]
],
"labelAt": [
1900,
1190
]
},
{
"id": "safe-observe",
"from": "safe",
"to": "observe",
"label": "Keep watching",
"fromSide": "top",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
2285,
20
],
[
950,
20
]
],
"labelAt": [
1640,
45
]
}
]
}
}
@@ -2,129 +2,313 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Measure skills adoption",
"title": "TO-BE: Measurement",
"output": "static/diagrams/strategy-measurement-mobile/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
880,
3630
]
},
"components": [
{
"id": "collect",
"type": "backend",
"label": "Establish baseline",
"sublabel": "Current workflow",
"id": "success-source",
"type": "external",
"label": "Tracker + acceptance",
"tag": "Matured task starts",
"sublabel": "Join evidence to the workflow",
"pos": [
20,
20
60,
120
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "validate",
"type": "backend",
"label": "Join evidence",
"sublabel": "Tasks / versions / costs",
"id": "success",
"type": "frontend",
"label": "Accepted / eligible starts \u00d7 100",
"tag": "Task success (%)",
"sublabel": "Failures + unresolved retained",
"pos": [
20,
140
60,
490
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "calculate",
"type": "backend",
"label": "Measure the pilot",
"sublabel": "Outcomes / quality",
"id": "quality-source",
"type": "external",
"label": "Changes + defects",
"tag": "Completed follow-up",
"sublabel": "Join evidence to the workflow",
"pos": [
20,
260
60,
860
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "balance",
"type": "backend",
"label": "Report together",
"sublabel": "Cost / confidence / gaps",
"id": "quality",
"type": "frontend",
"label": "Affected / accepted changes \u00d7 100",
"tag": "Escaped quality (%)",
"sublabel": "Count each affected change once",
"pos": [
20,
380
60,
1230
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "weigh",
"type": "backend",
"label": "Broader adoption",
"sublabel": "Availability + use",
"id": "cost-source",
"type": "external",
"label": "Invoices + review minutes",
"tag": "Include failed attempts",
"sublabel": "Join evidence to the workflow",
"pos": [
20,
500
60,
1600
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "decide",
"type": "backend",
"label": "Ongoing monitoring",
"sublabel": "Review + maintenance",
"id": "cost",
"type": "frontend",
"label": "Allocated cost / accepted tasks",
"tag": "Cost per accepted outcome",
"sublabel": "Missing buckets = partial",
"pos": [
20,
620
60,
1970
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "confidence-source",
"type": "external",
"label": "Anonymous 1\u20135 survey",
"tag": "Protect small groups",
"sublabel": "Join evidence to the workflow",
"pos": [
60,
2340
],
"size": [
740,
220
],
"icon": "none"
},
{
"id": "confidence",
"type": "frontend",
"label": "Confidence sum / valid responses",
"tag": "Verification confidence (1\u20135)",
"sublabel": "Show distribution + response rate",
"pos": [
60,
2710
],
"size": [
740,
220
],
"icon": "none"
},
{
"id": "interpret",
"type": "backend",
"label": "Compare with baseline",
"tag": "Interpret, then act",
"sublabel": "Like workflows + source coverage\nZero denominator = N/A\nTrends are not causal ROI\nFix gaps; improve the workflow",
"pos": [
60,
3080
],
"size": [
740,
400
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "collect",
"to": "validate",
"label": "prepare"
"id": "success-source-success",
"from": "success-source",
"to": "success",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
395
]
},
{
"id": "flow-1",
"from": "validate",
"to": "calculate",
"label": "collect"
"id": "quality-source-quality",
"from": "quality-source",
"to": "quality",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1135
]
},
{
"id": "flow-2",
"from": "calculate",
"to": "balance",
"label": "calculate"
"id": "cost-source-cost",
"from": "cost-source",
"to": "cost",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1875
]
},
{
"id": "flow-3",
"from": "balance",
"to": "weigh",
"label": "extend"
"id": "confidence-source-confidence",
"from": "confidence-source",
"to": "confidence",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
2615
]
},
{
"id": "flow-4",
"from": "weigh",
"to": "decide",
"label": "repeat"
"id": "success-interpret",
"from": "success",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
600.0
],
[
850,
3280.0
]
],
"labelAt": [
620,
3040
]
},
{
"id": "quality-interpret",
"from": "quality",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
1340.0
],
[
850,
3280.0
]
],
"labelAt": [
620,
3040
]
},
{
"id": "cost-interpret",
"from": "cost",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
2080.0
],
[
850,
3280.0
]
],
"labelAt": [
620,
3040
]
},
{
"id": "confidence-interpret",
"from": "confidence",
"to": "interpret",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
2985
]
}
]
}
}
+239 -77
View File
@@ -2,129 +2,291 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Measure skills adoption",
"title": "TO-BE: Measurement",
"output": "static/diagrams/strategy-measurement/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
2400,
1120
]
},
"components": [
{
"id": "collect",
"type": "backend",
"label": "Establish baseline",
"sublabel": "Current workflow",
"id": "success-source",
"type": "external",
"label": "Tracker + acceptance",
"tag": "Matured task starts",
"sublabel": "Join evidence to the workflow",
"pos": [
20,
20
50,
70
],
"size": [
180,
60
]
580,
180
],
"icon": "none"
},
{
"id": "validate",
"type": "backend",
"label": "Join evidence",
"sublabel": "Tasks / versions / costs",
"id": "success",
"type": "frontend",
"label": "Accepted / eligible starts \u00d7 100",
"tag": "Task success (%)",
"sublabel": "Failures + unresolved retained",
"pos": [
260,
20
770,
70
],
"size": [
180,
60
]
700,
180
],
"icon": "none"
},
{
"id": "calculate",
"type": "backend",
"label": "Measure the pilot",
"sublabel": "Outcomes / quality",
"id": "quality-source",
"type": "external",
"label": "Changes + defects",
"tag": "Completed follow-up",
"sublabel": "Join evidence to the workflow",
"pos": [
260,
150
50,
320
],
"size": [
180,
60
]
580,
180
],
"icon": "none"
},
{
"id": "balance",
"type": "backend",
"label": "Report together",
"sublabel": "Cost / confidence / gaps",
"id": "quality",
"type": "frontend",
"label": "Affected / accepted changes \u00d7 100",
"tag": "Escaped quality (%)",
"sublabel": "Count each affected change once",
"pos": [
20,
150
770,
320
],
"size": [
180,
60
]
700,
180
],
"icon": "none"
},
{
"id": "weigh",
"type": "backend",
"label": "Broader adoption",
"sublabel": "Availability + use",
"id": "cost-source",
"type": "external",
"label": "Invoices + review minutes",
"tag": "Include failed attempts",
"sublabel": "Join evidence to the workflow",
"pos": [
20,
280
50,
570
],
"size": [
180,
60
]
580,
180
],
"icon": "none"
},
{
"id": "decide",
"type": "backend",
"label": "Ongoing monitoring",
"sublabel": "Review + maintenance",
"id": "cost",
"type": "frontend",
"label": "Allocated cost / accepted tasks",
"tag": "Cost per accepted outcome",
"sublabel": "Missing buckets = partial",
"pos": [
260,
280
770,
570
],
"size": [
180,
60
]
700,
180
],
"icon": "none"
},
{
"id": "confidence-source",
"type": "external",
"label": "Anonymous 1\u20135 survey",
"tag": "Protect small groups",
"sublabel": "Join evidence to the workflow",
"pos": [
50,
820
],
"size": [
580,
180
],
"icon": "none"
},
{
"id": "confidence",
"type": "frontend",
"label": "Confidence sum / valid responses",
"tag": "Verification confidence (1\u20135)",
"sublabel": "Show distribution + response rate",
"pos": [
770,
820
],
"size": [
700,
180
],
"icon": "none"
},
{
"id": "interpret",
"type": "backend",
"label": "Compare with baseline",
"tag": "Interpret, then act",
"sublabel": "Like workflows + source coverage\nZero denominator = N/A\nTrends are not causal ROI\nFix gaps; improve the workflow",
"pos": [
1650,
325
],
"size": [
680,
400
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "collect",
"to": "validate",
"label": "prepare"
"id": "success-source-success",
"from": "success-source",
"to": "success",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "flow-1",
"from": "validate",
"to": "calculate",
"label": "collect"
"id": "quality-source-quality",
"from": "quality-source",
"to": "quality",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "flow-2",
"from": "calculate",
"to": "balance",
"label": "calculate"
"id": "cost-source-cost",
"from": "cost-source",
"to": "cost",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "flow-3",
"from": "balance",
"to": "weigh",
"label": "extend"
"id": "confidence-source-confidence",
"from": "confidence-source",
"to": "confidence",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "flow-4",
"from": "weigh",
"to": "decide",
"label": "repeat"
"id": "success-interpret",
"from": "success",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1550,
160
],
[
1550,
525
]
]
},
{
"id": "quality-interpret",
"from": "quality",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1550,
410
],
[
1550,
525
]
]
},
{
"id": "cost-interpret",
"from": "cost",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1550,
660
],
[
1550,
525
]
]
},
{
"id": "confidence-interpret",
"from": "confidence",
"to": "interpret",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1550,
910
],
[
1550,
525
]
]
}
]
}
}
@@ -2,129 +2,261 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Risk-tiered qualification",
"title": "TO-BE: Quality",
"output": "static/diagrams/strategy-quality-mobile/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
880,
2800
]
},
"components": [
{
"id": "risk",
"type": "security",
"label": "Classify risk",
"sublabel": "R1 / R2 / R3",
"id": "selection",
"type": "external",
"label": "Right skill?",
"tag": "Selection evidence",
"sublabel": "Triggers, near misses, negatives",
"pos": [
20,
20
60,
120
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "plan",
"type": "security",
"label": "Agree evaluation",
"sublabel": "Task mix + thresholds",
"id": "safety",
"type": "external",
"label": "Safe execution?",
"tag": "Safety evidence",
"sublabel": "Permissions + adversarial trials",
"pos": [
20,
140
60,
490
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "trial",
"type": "security",
"label": "Repeat trials",
"sublabel": "Candidate vs approved",
"id": "artifact",
"type": "external",
"label": "Valid artifact?",
"tag": "Independent inspection",
"sublabel": "Tests + human assessment",
"pos": [
20,
260
60,
860
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "verify",
"type": "security",
"label": "Verify artifacts",
"sublabel": "Independent checks",
"id": "outcome",
"type": "external",
"label": "Useful outcome?",
"tag": "Workflow evidence",
"sublabel": "Baseline + repeated trials",
"pos": [
20,
380
60,
1230
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "gate",
"type": "security",
"label": "All gates pass?",
"sublabel": "Safety cannot be traded",
"label": "Every gate must pass",
"tag": "Risk sets evaluation depth",
"sublabel": "No averaging away harm\nCritical effect blocks promotion\nMissing coverage = incomplete",
"pos": [
20,
500
60,
1600
],
"size": [
180,
60
]
740,
310
],
"icon": "none"
},
{
"id": "promote",
"type": "security",
"label": "Promote or hold",
"sublabel": "Owner decision",
"id": "accept",
"type": "backend",
"label": "Accept evidence",
"tag": "All mandatory gates passed",
"sublabel": "Proceed to scoped approval",
"pos": [
20,
620
60,
2060
],
"size": [
180,
60
]
740,
220
],
"icon": "none"
},
{
"id": "rework",
"type": "security",
"label": "Rework / retest",
"tag": "Any failure or missing evidence",
"sublabel": "Keep failures in the record",
"pos": [
60,
2430
],
"size": [
740,
220
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "risk",
"to": "plan",
"label": "Scope"
},
{
"id": "flow-1",
"from": "plan",
"to": "trial",
"label": "freeze plan"
},
{
"id": "flow-2",
"from": "trial",
"to": "verify",
"label": "Assess"
},
{
"id": "flow-3",
"from": "verify",
"id": "selection-gate",
"from": "selection",
"to": "gate",
"label": "review evidence"
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
230.0
],
[
850,
1755.0
]
],
"labelAt": [
620,
1560
]
},
{
"id": "flow-4",
"id": "safety-gate",
"from": "safety",
"to": "gate",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
600.0
],
[
850,
1755.0
]
],
"labelAt": [
620,
1560
]
},
{
"id": "artifact-gate",
"from": "artifact",
"to": "gate",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
970.0
],
[
850,
1755.0
]
],
"labelAt": [
620,
1560
]
},
{
"id": "outcome-gate",
"from": "outcome",
"to": "gate",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1505
]
},
{
"id": "gate-accept",
"from": "gate",
"to": "promote",
"label": "decide"
"to": "accept",
"label": "Pass",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1965
]
},
{
"id": "gate-rework",
"from": "gate",
"to": "rework",
"label": "Fail",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
824,
1755.0
],
[
824,
2540.0
]
],
"labelAt": [
620,
2390
]
}
]
}
}
+215 -79
View File
@@ -2,129 +2,265 @@
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "TO-BE: Risk-tiered qualification",
"title": "TO-BE: Quality",
"output": "static/diagrams/strategy-quality/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
}
},
"viewBox": [
2000,
1050
]
},
"components": [
{
"id": "risk",
"type": "security",
"label": "Classify risk",
"sublabel": "R1 / R2 / R3",
"id": "selection",
"type": "external",
"label": "Right skill?",
"tag": "Selection evidence",
"sublabel": "Triggers, near misses, negatives",
"pos": [
20,
20
60,
70
],
"size": [
180,
60
]
510,
170
],
"icon": "none"
},
{
"id": "plan",
"type": "security",
"label": "Agree evaluation",
"sublabel": "Task mix + thresholds",
"id": "safety",
"type": "external",
"label": "Safe execution?",
"tag": "Safety evidence",
"sublabel": "Permissions + adversarial trials",
"pos": [
260,
20
60,
310
],
"size": [
180,
60
]
510,
170
],
"icon": "none"
},
{
"id": "trial",
"type": "security",
"label": "Repeat trials",
"sublabel": "Candidate vs approved",
"id": "artifact",
"type": "external",
"label": "Valid artifact?",
"tag": "Independent inspection",
"sublabel": "Tests + human assessment",
"pos": [
260,
150
60,
550
],
"size": [
180,
60
]
510,
170
],
"icon": "none"
},
{
"id": "verify",
"type": "security",
"label": "Verify artifacts",
"sublabel": "Independent checks",
"id": "outcome",
"type": "external",
"label": "Useful outcome?",
"tag": "Workflow evidence",
"sublabel": "Baseline + repeated trials",
"pos": [
20,
150
60,
790
],
"size": [
180,
60
]
510,
170
],
"icon": "none"
},
{
"id": "gate",
"type": "security",
"label": "All gates pass?",
"sublabel": "Safety cannot be traded",
"label": "Every gate must pass",
"tag": "Risk sets evaluation depth",
"sublabel": "No averaging away harm\nCritical effect blocks promotion\nMissing coverage = incomplete",
"pos": [
20,
280
740,
350
],
"size": [
180,
60
]
520,
310
],
"icon": "none"
},
{
"id": "promote",
"type": "security",
"label": "Promote or hold",
"sublabel": "Owner decision",
"id": "accept",
"type": "backend",
"label": "Accept evidence",
"tag": "All mandatory gates passed",
"sublabel": "Proceed to scoped approval",
"pos": [
260,
280
1430,
160
],
"size": [
180,
60
]
510,
190
],
"icon": "none"
},
{
"id": "rework",
"type": "security",
"label": "Rework / retest",
"tag": "Any failure or missing evidence",
"sublabel": "Keep failures in the record",
"pos": [
1430,
760
],
"size": [
510,
190
],
"icon": "none"
}
],
"connections": [
{
"id": "flow-0",
"from": "risk",
"to": "plan",
"label": "Scope"
},
{
"id": "flow-1",
"from": "plan",
"to": "trial",
"label": "freeze plan"
},
{
"id": "flow-2",
"from": "trial",
"to": "verify",
"label": "Assess"
},
{
"id": "flow-3",
"from": "verify",
"id": "selection-gate",
"from": "selection",
"to": "gate",
"label": "review evidence"
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
650,
155
],
[
650,
505
]
]
},
{
"id": "flow-4",
"id": "safety-gate",
"from": "safety",
"to": "gate",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
650,
395
],
[
650,
505
]
]
},
{
"id": "artifact-gate",
"from": "artifact",
"to": "gate",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
650,
635
],
[
650,
505
]
]
},
{
"id": "outcome-gate",
"from": "outcome",
"to": "gate",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
650,
875
],
[
650,
505
]
]
},
{
"id": "gate-accept",
"from": "gate",
"to": "promote",
"label": "decide"
"to": "accept",
"label": "Pass",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1340,
505
],
[
1340,
255
]
],
"labelAt": [
1330,
210
]
},
{
"id": "gate-rework",
"from": "gate",
"to": "rework",
"label": "Fail",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
1340,
505
],
[
1340,
855
]
],
"labelAt": [
1325,
900
]
}
]
}
}
+3
View File
@@ -29,6 +29,9 @@ Include `title` and `description` frontmatter. Place each page in its owning sec
| Portal maintenance and reference | `docs/core/` | `sidebars.ts` |
Keep these content roots disjoint. Preserve existing URLs when moving pages. Specific AI initiatives and organizational reporting lines remain subject to agreement.
Every published documentation page must have a navigable sidebar entry. Nested references are welcome; hidden documentation is not. Linked category overviews count as entries, but an in-page anchor is not a separate document. Portal-maintenance pages keep their existing `/docs/` routes and dedicated sidebar, reachable from Contribution → Portal maintenance.
`pnpm verify:build` compares all Docusaurus-generated document IDs/routes with resolved sidebar trees (including linked category overviews), checks reachability from the navbar, rejects missing/duplicate entries and unclassified HTML routes, and checks every document’s local links and anchors. The inventory is written to `.cache/sidebar-coverage.json`. Only the homepage, not-found page, search-results UI, legacy Strategy redirect and standalone diagram viewers are non-document HTML routes; no documentation page is exempt.
## Validate the change
+1 -1
View File
@@ -1,7 +1,7 @@
---
id: measurement
title: Skills Measurement
sidebar_label: Skills Measurement
sidebar_label: Measurement
description: Which KPIs to collect during skills adoption, where the evidence comes from, how to calculate them and when to report.
hide_table_of_contents: true
---
+1 -1
View File
@@ -68,6 +68,6 @@ Stage 2 builds on proven skills, measurements and working practices—not permis
</details>
**Next:** [Return to the strategy destination →](./target-operating-model.mdx)
**Next:** [Stage - 01 - Skills Adoption →](./stage-01-skills-adoption.mdx)
</div>
+1 -1
View File
@@ -44,6 +44,6 @@ Owner ratification is required before adoption. Review earlier after incidents o
</details>
**Next:** [Use exit evidence to choose the next rollout stage →](./roadmap.mdx)
**Next:** [Stage - 02 - Autonomous Agent →](./stage-02-autonomous-agent.mdx)
</div>
@@ -0,0 +1,36 @@
---
id: stage-01-skills-adoption
title: Stage - 01 - Skills Adoption
description: Build proven skills and measurable working practices through human and AI Agent collaboration, starting with Delivery.
hide_table_of_contents: true
---
<div className="strategy-content">
**Human + AI Agent → proven skills and working practices → future Autonomous Agent.**
This first stage of the [Roadmap](./roadmap.mdx) standardizes skills in real human + AI Agent work. It builds the foundation for [Target](./target-operating-model.mdx), while people direct the work, verify outcomes and improve the skills.
## Delivery first
Start with the engineering path: codebase structure and existing principles → system-logic source → tests → builds and packaging → deployments. Understand actual practices, tooling, controls and problems before changing them.
Adoption then extends to other SDLC phases; their order is not yet decided. Delivery keeps improving as adoption expands.
## Repeat within each phase
**Understand the current situation → establish KPIs and a baseline → engineer skills topology → use skills in real work → collect metrics and feedback → understand the new current situation → repeat.**
Skills topology connects what skills exist, how they relate, when they are used and how they work together. Compare accepted outcomes, quality, cost and review effort with the baseline; use friction and feedback to revise the skills and working practices. These are repeating steps within each phase, not additional roadmap stages.
## Put the foundation into practice
- [Governance](./skills-governance.mdx) — define ownership, authority and approval of skills.
- [Quality](./skills-quality.mdx) — qualify skills and verify their outcomes before wider use.
- [Adoption](./skills-adoption.mdx) — coach bounded workflows with a supported client and a manual path.
- [Measurement](./measurement.mdx) — establish KPIs, collect evidence and compare outcomes with the baseline.
- [Maintenance](./skills-maintenance.mdx) — review feedback, control versions and recover from unsafe updates.
Proven skills, measurements and working practices support [Stage - 02 - Autonomous Agent](./stage-02-autonomous-agent.mdx); adoption alone does not grant autonomous execution authority.
</div>
@@ -0,0 +1,24 @@
---
id: stage-02-autonomous-agent
title: Stage - 02 - Autonomous Agent
description: Introduce the future Autonomous Agent on proven skills and measured practices while humans retain direction and review.
hide_table_of_contents: true
---
<div className="strategy-content">
**Proven skills and measured practices → future Autonomous Agent → human-directed SDLC.**
This future stage of the [Roadmap](./roadmap.mdx) builds on [Stage - 01 - Skills Adoption](./stage-01-skills-adoption.mdx). The destination is [Target](./target-operating-model.mdx): an Autonomous Agent that executes and governs the SDLC cycle, with humans retaining direction and review.
## Build on what has been proven
Carry forward proven skills, measurements and working practices from real human + AI Agent work. The skills foundation provides [governance](./skills-governance.mdx), [quality checks](./skills-quality.mdx), [adoption feedback](./skills-adoption.mdx), [measurement](./measurement.mdx) and [maintenance](./skills-maintenance.mdx); it is not permission to expand authority automatically.
## Introduce authority deliberately
Establish reliable observation and evidence assembly, then proposing and routing work, before granting scoped execution authority. The Roadmap describes recommended controls and hold conditions, including exact-artifact approvals, independent review, recovery and human takeover. Sensitive decisions remain human; missing evidence never means permission.
This is future direction, not deployed autonomy, agreed delivery dates or approved KPI thresholds. The transition depends on reviewed evidence and explicit authority decisions, not adoption volume alone.
</div>
+1 -1
View File
@@ -162,6 +162,6 @@ Success means accepted outcomes with maintained quality, controlled risk and und
</details>
**Next:** [Decide who owns and approves each skill →](./skills-governance.mdx)
**Next:** [Follow the Roadmap toward Target →](./roadmap.mdx)
</div>
+1 -1
View File
@@ -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 && node scripts/browser/sdlc-cycle.cjs && node scripts/browser/target-midpoints.cjs && node scripts/browser/skills-measurement.cjs && node scripts/browser/roadmap.cjs && node scripts/browser/roadmap-timeline.cjs",
"test:browser": "node scripts/browser/sidebar-coverage.cjs && node scripts/browser/skills-stories.cjs && node scripts/browser/strategy-navigation.cjs && node scripts/browser/strategy-story.cjs && node scripts/browser/inline-controls.cjs && node scripts/browser/sdlc-cycle.cjs && node scripts/browser/target-midpoints.cjs && node scripts/browser/skills-measurement.cjs && node scripts/browser/roadmap.cjs && node scripts/browser/roadmap-timeline.cjs",
"kpi:example": "node scripts/ai-kpi.mjs"
},
"dependencies": {
+2 -2
View File
@@ -125,9 +125,9 @@ 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/skills-governance/"]').filter({visible:true}).focus();
await page.locator('.strategy-content a[href$="/strategy/roadmap/"]').filter({visible:true}).focus();
await page.keyboard.press('Enter');
await page.waitForURL('**/strategy/skills-governance/');
await page.waitForURL('**/strategy/roadmap/');
await page.goBack({waitUntil:'domcontentloaded'});
await page.waitForURL('**/strategy/target-operating-model/');
await page.getByRole('heading', {name:'Target operating model', exact:true, level:1}).waitFor();
+1 -1
View File
@@ -23,7 +23,7 @@ const fs = require('node:fs');
if (width < 700) await page.getByRole('button', {name:'Toggle navigation bar'}).click();
const menu = page.locator('.theme-doc-sidebar-menu .menu__link').filter({visible:true});
await menu.first().waitFor({state:'visible'});
assert.deepEqual(await menu.allTextContents(), ['Target', 'Roadmap', 'Skills governance', 'Skills quality', 'Skills adoption', 'Skills Measurement', 'Skills maintenance']);
assert.deepEqual(await menu.allTextContents(), ['Target', 'Roadmap', 'Stage - 01 - Skills Adoption', 'Stage - 02 - Autonomous Agent']);
assert((await menu.nth(0).getAttribute('href')).endsWith('/strategy/target-operating-model/'));
assert((await menu.nth(1).getAttribute('href')).endsWith('/strategy/roadmap/'));
assert.equal(await menu.filter({hasText:/^Target$/}).count(), 1);
+60
View File
@@ -0,0 +1,60 @@
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/sidebar-browser-${process.argv[3] || 'local'}`;
const {documents} = JSON.parse(fs.readFileSync('.cache/sidebar-coverage.json', 'utf8'));
assert(documents.length > 0);
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:900}});
try {
const page = await context.newPage();
const errors = [];
page.on('pageerror', e => errors.push(e.message));
const menu = page.locator('.theme-doc-sidebar-menu').filter({visible:true});
const open = async () => {
if (width < 997) await page.getByRole('button', {name:'Toggle navigation bar'}).click();
await menu.waitFor();
};
for (const doc of documents) {
assert.equal((await page.goto(`${base}${doc.route}/`, {waitUntil:'networkidle'})).status(), 200);
await page.locator('article h1').waitFor();
await open();
const links = menu.locator('a[href]:not([role="button"])').filter({visible:true});
const active = await links.evaluateAll((nodes, route) => nodes.filter(n => n.pathname.replace(/\/$/,'') === route).map(n => n.textContent), doc.route);
assert.equal(active.length, 1, `${width}: visible unique owning-sidebar entry for ${doc.route}`);
results.push({width, route:doc.route, visibleEntry:active[0]});
}
// Contribution exposes all legacy maintenance pages without changing routes.
for (const [label, route] of [['Starting point','overview'],['Update and publish','maintain'],['Site configuration','reference']]) {
await page.goto(`${base}/eldorado-v1/documentation/contribution/`, {waitUntil:'networkidle'});
await open();
const toggle = menu.getByRole('button', {name:'Portal maintenance', exact:true});
await toggle.focus();
await page.keyboard.press('Enter');
await menu.getByRole('link', {name:label, exact:true}).click();
await page.waitForURL(`${base}/eldorado-v1/documentation/docs/${route}/`);
await page.locator('article h1').waitFor();
}
const reference = '/eldorado-v1/documentation/strategy/reference/measurement-contracts/';
await page.goto(`${base}${reference}#supported-bundle-coverage`, {waitUntil:'networkidle'});
await page.locator('#supported-bundle-coverage').waitFor();
assert((await page.locator('article').innerText()).includes('Supported bundle coverage'));
assert.equal(await page.locator('.breadcrumbs a').filter({hasText:'Stage - 01 - Skills Adoption'}).getAttribute('href'), '/eldorado-v1/documentation/strategy/stage-01-skills-adoption/');
assert((await page.locator('.breadcrumbs').innerText()).includes('Reference'));
await open();
await menu.getByRole('link', {name:'Measurement Contracts', exact:true}).waitFor({state:'visible'});
await page.screenshot({path:`${output}/${width}-reference.png`, animations:'disabled'});
assert.deepEqual(errors, []);
} finally {await context.close();}
}
assert.equal(results.length, documents.length * 3);
fs.writeFileSync(`${output}/results.json`, JSON.stringify(results,null,2));
console.log(`PASS: all ${documents.length} docs have visible unique sidebar entries at 3 viewports; Contribution links, Reference breadcrumbs and reported anchor work`);
} finally {await browser.close();}
})().catch(e => {console.error(e); process.exitCode = 1;});
+42
View File
@@ -0,0 +1,42 @@
const {chromium}=require('playwright');
const fs=require('node:fs');
const path=require('node:path');
const assert=require('node:assert/strict');
(async()=>{
const dir=process.argv[2]||'.cache/skills-candidate';
const browser=await chromium.launch({executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']});
const results=[];
try {
for(const topic of ['governance','quality','adoption','measurement','maintenance']) 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`));
const measured=await page.locator('svg').evaluate(svg=>{
const v=svg.viewBox.baseVal;const issues=[];
for(const el of svg.querySelectorAll('[data-node-id],path[data-edge-id],g[data-edge-id]')){
const b=el.getBBox();if(b.x<v.x-1||b.y<v.y-1||b.x+b.width>v.x+v.width+1||b.y+b.height>v.y+v.height+1)issues.push(el.getAttribute('data-node-id')||el.getAttribute('data-edge-id'));
}
// Measure the whole rendered label group, including its opaque mask:
// 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=[];
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};
});
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`);
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)await page.screenshot({path:`${dir}/${topic}-standalone-export.png`});
results.push({topic,width,nodes:measured.nodes.length,edges:measured.edges.length,complete:true});await page.close();
}
fs.writeFileSync(`${dir}/export-results.json`,JSON.stringify(results,null,2));
console.log(`PASS ${results.length} standalone SVGs: exact graph, text, full-canvas containment and non-overlapping edge labels`);
}finally{await browser.close();}
})().catch(e=>{console.error(e);process.exitCode=1});
+3 -3
View File
@@ -22,7 +22,7 @@ const fs = require('node:fs');
if (width < 700) await page.getByRole('button', {name:'Toggle navigation bar'}).click();
const menu = page.locator('.theme-doc-sidebar-menu .menu__link').filter({visible:true});
await menu.first().waitFor({state:'visible'});
assert.deepEqual(await menu.allTextContents(), ['Target','Roadmap','Skills governance','Skills quality','Skills adoption','Skills Measurement','Skills maintenance']);
assert.deepEqual(await menu.allTextContents(), ['Target','Roadmap','Stage - 01 - Skills Adoption','Governance','Quality','Adoption','Measurement','Maintenance','Reference','Stage - 02 - Autonomous Agent']);
if (width < 700) await page.getByRole('button', {name:'Close navigation bar'}).click();
const panel = page.locator('.archify-panel--story');
const iframe = panel.locator(width > 700 ? '.story-desktop' : '.story-mobile');
@@ -33,7 +33,7 @@ const fs = require('node:fs');
const nodes = [...svg.querySelectorAll('[data-node-id]')].map(n => n.getBoundingClientRect().toJSON());
return {nodes, width:innerWidth, height:innerHeight, minLabel:Math.min(...[...svg.querySelectorAll('text[data-node-label]')].map(n => parseFloat(getComputedStyle(n).fontSize)*n.getScreenCTM().a))};
});
assert.equal(geometry.nodes.length, 6);
assert.equal(geometry.nodes.length, JSON.parse(fs.readFileSync('diagrams/strategy-measurement.architecture.json')).components.length);
for (const n of geometry.nodes) assert(n.x >= 0 && n.y >= 0 && n.right <= geometry.width + 1 && n.bottom <= geometry.height + 1);
assert(geometry.minLabel >= 10, `readable node labels: ${geometry.minLabel}`);
await frame.locator('#btn-theme').click();
@@ -50,7 +50,7 @@ const fs = require('node:fs');
assert.equal(await download.failure(), null);
const path = `${output}/${width}.svg`; await download.saveAs(path);
const svg = fs.readFileSync(path, 'utf8');
assert(svg.includes('Establish baseline') && svg.includes('Ongoing monitoring'));
assert(svg.includes('Compare with baseline') && svg.includes('Zero denominator = N/A'));
const opener = panel.getByRole('button', {name:/^Expand /});
await opener.click();
const close = panel.getByRole('button', {name:'Close full-screen diagram', exact:true});
+46
View File
@@ -0,0 +1,46 @@
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/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 ['governance','quality','adoption','measurement','maintenance']) for(const width of [1440,1024,390,320]) {
const page=await browser.newPage({viewport:{width,height:900},colorScheme:'light'});const errors=[];page.on('pageerror',e=>errors.push(e.message));
const route=topic==='measurement'?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();
const frame=await(await iframe.elementHandle()).contentFrame();await frame.locator('svg[role="img"]').waitFor();
const geometry=await frame.locator('svg[role="img"]').evaluate(svg=>{
const nodes=[...svg.querySelectorAll('g[data-node-id]')];const issues=[];let min=Infinity;
for(const n of nodes){const rect=n.querySelector('rect').getBBox();for(const t of n.querySelectorAll('text')){
const b=t.getBBox();min=Math.min(min,parseFloat(getComputedStyle(t).fontSize)*t.getScreenCTM().a);
if(b.x<rect.x||b.y<rect.y||b.x+b.width>rect.x+rect.width||b.y+b.height>rect.y+rect.height)issues.push(n.id+':'+t.textContent);
}}
return {nodes:nodes.length,min,issues,width:innerWidth,svgWidth:svg.getBoundingClientRect().width};
});
const source=JSON.parse(fs.readFileSync(`diagrams/strategy-${topic}.architecture.json`));
assert.equal(geometry.nodes,source.components.length);assert.deepEqual(geometry.issues,[]);assert(geometry.min>=10,JSON.stringify(geometry));
for(const theme of ['light','dark']){
if(await frame.locator('html').getAttribute('data-theme')!==theme)await frame.locator('#btn-theme').click();
await page.waitForFunction(t=>document.documentElement.dataset.theme===t,theme);
await panel.scrollIntoViewIfNeeded();await page.screenshot({path:`${out}/${topic}-${width}-${theme}-inline.png`});
const open=panel.getByRole('button',{name:/^Expand /});await open.click();const close=panel.getByRole('button',{name:'Close full-screen diagram',exact:true});await close.waitFor({state:'visible'});
const expanded=await(await panel.locator('dialog iframe').elementHandle()).contentFrame();await expanded.locator('svg[role="img"]').waitFor();
assert((await expanded.url()).includes(width<=1100?`${topic}-mobile/`:`${topic}/`));
await page.screenshot({path:`${out}/${topic}-${width}-${theme}-fullscreen.png`});
if(width<=700){await page.setViewportSize({width,height:480});assert(await panel.locator('dialog').evaluate(n=>n.scrollHeight>n.clientHeight));await panel.locator('dialog').evaluate(n=>n.scrollTop=n.scrollHeight);await page.screenshot({path:`${out}/${topic}-${width}-${theme}-short-bottom.png`});await page.setViewportSize({width,height:900});}
await close.focus();await page.keyboard.press('Escape');assert(await open.evaluate(n=>n===document.activeElement));
}
await frame.locator('#btn-preset').click();await frame.locator('[data-preset-value="blueprint"]').click();
await frame.locator('#btn-preset').click();await frame.locator('[data-preset-value="classic"]').click();
await frame.locator('#btn-export').click();const [download]=await Promise.all([page.waitForEvent('download'),frame.locator('[data-format="svg"]').click()]);assert.equal(await download.failure(),null);
await download.saveAs(`${out}/${topic}-${width}.svg`);assert(fs.readFileSync(`${out}/${topic}-${width}.svg`,'utf8').includes('eldorado-skills-typography'));
assert(!await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth));assert.deepEqual(errors,[]);
results.push({topic,width,geometry,controls:true,export:true,themes:true,fullscreen:true});fs.writeFileSync(`${out}/results.json`,JSON.stringify(results,null,2));await page.close();
}
console.log(`PASS ${results.length} Skills story contexts: geometry, themes, controls, exports, fullscreen and short-phone scrolling`);
}finally{await browser.close();}
})().catch(e=>{console.error(e);process.exitCode=1});
+89
View File
@@ -0,0 +1,89 @@
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/strategy-navigation-${process.argv[3] || 'local'}`;
fs.mkdirSync(output, {recursive:true});
const prefix = '/eldorado-v1/documentation/strategy/';
const first = 'Stage - 01 - Skills Adoption';
const second = 'Stage - 02 - Autonomous Agent';
const topics = ['Governance','Quality','Adoption','Measurement','Maintenance','Reference'];
const ids = ['target-operating-model','roadmap','stage-01-skills-adoption','skills-governance','skills-quality','skills-adoption','measurement','skills-maintenance','reference/measurement-contracts','stage-02-autonomous-agent'];
const titles = ['Target','Roadmap',first,'Skills governance','Skills quality','Skills adoption','Skills Measurement','Skills maintenance','Skills KPI reference',second];
const browser = await chromium.launch({headless:true, executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, args:['--no-sandbox']});
const results = [];
try {
for (const width of [1440,1024,390,320]) {
const context = await browser.newContext({viewport:{width,height:900}, colorScheme:'light'});
try {
const page = await context.newPage();
const errors = [];
page.on('pageerror', error => errors.push(error.message));
const openMenu = async () => {
if (width < 997) await page.getByRole('button', {name:'Toggle navigation bar'}).click();
const menu = page.locator('.theme-doc-sidebar-menu').filter({visible:true});
await menu.locator('.menu__link').first().waitFor({state:'visible'});
return menu;
};
const closeMenu = async () => {
if (width < 997) await page.getByRole('button', {name:'Close navigation bar'}).click();
};
assert.equal((await page.goto(`${base}${prefix}roadmap/`, {waitUntil:'networkidle'})).status(), 200);
let menu = await openMenu();
const visibleLabels = () => menu.locator('.menu__link').filter({visible:true}).allTextContents();
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,second]);
const category = menu.locator('.theme-doc-sidebar-item-category').filter({has:page.getByRole('link', {name:first, exact:true})});
const caret = category.locator(':scope > .menu__list-item-collapsible > button.menu__caret');
await caret.click();
await category.getByRole('link', {name:'Governance', exact:true}).waitFor({state:'visible'});
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,second]);
assert.deepEqual(await category.locator('ul .menu__link').filter({visible:true}).allTextContents(), topics);
const reference = category.getByRole('button', {name:'Reference', exact:true});
await reference.focus();
await page.keyboard.press('Enter');
await category.getByRole('link', {name:'Measurement Contracts', exact:true}).waitFor({state:'visible'});
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,'Measurement Contracts',second]);
await reference.click();
await page.screenshot({path:`${output}/${width}-expanded.png`, animations:'disabled'});
await caret.click();
await category.getByRole('link', {name:'Governance', exact:true}).waitFor({state:'hidden'});
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,second]);
await menu.getByRole('link', {name:first, exact:true}).click();
await page.waitForURL(`${base}${prefix}stage-01-skills-adoption/`);
await page.getByRole('heading', {level:1, name:first, exact:true}).waitFor();
assert.equal(await page.locator('h1').innerText(), first);
assert((await page.locator('article').innerText()).includes('Delivery first'));
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth));
await page.screenshot({path:`${output}/${width}-stage-01.png`, fullPage:true, animations:'disabled'});
for (const [index, id] of ids.entries()) {
assert.equal((await page.goto(`${base}${prefix}${id}/`, {waitUntil:'networkidle'})).status(), 200);
assert.equal(new URL(page.url()).pathname, `${prefix}${id}/`);
// Target has a longer canonical page heading but keeps its short navigation label.
if (index) assert.equal(await page.locator('h1').innerText(), titles[index]);
else assert((await page.locator('h1').innerText()).includes('Target'));
const prev = page.locator('.pagination-nav__link--prev');
const next = page.locator('.pagination-nav__link--next');
if (index) assert.equal(await prev.getAttribute('href'), `${prefix}${ids[index-1]}/`);
else assert.equal(await prev.count(), 0);
if (index < ids.length-1) assert.equal(await next.getAttribute('href'), `${prefix}${ids[index+1]}/`);
else assert.equal(await next.count(), 0);
if (index >= 3 && index <= 7) {
assert.equal(await page.locator('.breadcrumbs a').filter({hasText:first}).getAttribute('href'), `${prefix}stage-01-skills-adoption/`);
menu = await openMenu();
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,second]);
await closeMenu();
}
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), `${width}: overflow on ${id}`);
}
assert((await page.locator('article').innerText()).includes('not deployed autonomy'));
await page.screenshot({path:`${output}/${width}-stage-02.png`, fullPage:true, animations:'disabled'});
assert.deepEqual(errors, []);
results.push({width, routes:ids.length, linkedCategory:true, expandCollapse:true, nesting:true, breadcrumbs:true, pagination:true, overflow:false, errors});
fs.writeFileSync(`${output}/results.json`, JSON.stringify(results,null,2));
} finally {await context.close();}
}
assert.equal(results.length, 4);
console.log('PASS: 4 desktop/mobile viewports; all 10 routes; linked category, keyboard expansion, nested Reference, breadcrumbs, next/previous and no overflow/errors');
} finally {await browser.close();}
})().catch(error => {console.error(error); process.exitCode = 1;});
+3 -1
View File
@@ -19,7 +19,9 @@ 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>=(id==='roadmap'?2:5) && geometry.nodes.length<=(['index','target-operating-model'].includes(id)?18:7),id);
const name=await panel.getAttribute('data-diagram');
const source=JSON.parse(fs.readFileSync(`diagrams/${name}.${name==='strategy-roadmap'?'workflow':'architecture'}.json`));
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'?1100:id==='roadmap'?600:200),id);assert(!data.overflow,id);assert.equal(data.openDetails,0,id);
+8 -6
View File
@@ -1,6 +1,7 @@
import {readdirSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, renameSync, rmSync} from 'node:fs';
import {join} from 'node:path';
import {execFileSync} from 'node:child_process';
import {skillsViewer, isSkillsStory} from './skills-viewer.mjs';
import {roadmapViewer} from './roadmap-viewer.mjs';
import {targetRouting} from './target-routing.mjs';
import {createHash} from 'node:crypto';
@@ -27,11 +28,12 @@ for (const file of readdirSync('diagrams').sort()) {
if (spec.diagram_type !== type || spec.meta.output !== output) throw new Error(`Diagram type/output mismatch: ${file}`);
const run = args => execFileSync(process.execPath,[cli,...args],{stdio:'inherit',env:{...process.env,ARCHIFY_UPDATE_CHECK_DISABLED:'1'}});
const roadmap = /^strategy-roadmap(?:-(?:adoption|loop))?(?:-mobile)?$/.test(name);
if (roadmap) {
const skills = isSkillsStory(name);
if (roadmap || skills) {
// Native renderer validates schema and compiles layout. Check readability
// after multiline typography, not upstream's single-line preview.
execFileSync(process.execPath, ['vendor/archify/renderers/workflow/render-workflow.mjs', input, output], {stdio:'inherit', env:{...process.env, ARCHIFY_UPDATE_CHECK_DISABLED:'1'}});
writeFileSync(output, roadmapViewer(readFileSync(output, 'utf8'), spec));
execFileSync(process.execPath, [`vendor/archify/renderers/${type}/render-${type}.mjs`, input, output], {stdio:'inherit', env:{...process.env, ARCHIFY_UPDATE_CHECK_DISABLED:'1'}});
writeFileSync(output, (skills ? skillsViewer : roadmapViewer)(readFileSync(output, 'utf8'), spec));
run(['check',output,'--json']);
} else {
run(['validate',type,input,'--quality','showcase','--json']);
@@ -61,13 +63,13 @@ for (const file of readdirSync('diagrams').sort()) {
}
writeFileSync(output, viewer.replace('</head>', `<style id="eldorado-inline-controls">${inlineStyles}</style><style id="eldorado-cycle-labels">${storyStyles}</style></head>`)
.replace('</body>', `<script>${themeBridge}</script></body>`));
if (roadmap) {
if (roadmap || skills) {
// This is a portal receipt, not upstream deliver provenance: formatting
// happens between the native schema/layout compiler and artifact checker.
const hash = path => createHash('sha256').update(readFileSync(path)).digest('hex');
const checked = JSON.parse(execFileSync(process.execPath, [cli,'check',output,'--json'], {encoding:'utf8', env:{...process.env, ARCHIFY_UPDATE_CHECK_DISABLED:'1'}}));
if (!checked.ok || checked.composition?.status !== 'pass') throw new Error('Roadmap final artifact failed');
writeFileSync(`.cache/archify/${name}.portal.json`, JSON.stringify({source:input, sourceSha256:hash(input), adapterSha256:hash('scripts/roadmap-viewer.mjs'), artifactSha256:hash(output), checker:checked}, null, 2));
if (!checked.ok || checked.composition?.status !== 'pass') throw new Error('Story final artifact failed');
writeFileSync(`.cache/archify/${name}.portal.json`, JSON.stringify({source:input, sourceSha256:hash(input), adapterSha256:hash(skills ? 'scripts/skills-viewer.mjs' : 'scripts/roadmap-viewer.mjs'), artifactSha256:hash(output), checker:checked}, null, 2));
} else {
renameSync(`static/diagrams/${name}/index.delivery.json`, `.cache/archify/${name}.delivery.json`);
if (name === 'strategy-target') {
+1 -1
View File
@@ -5,7 +5,7 @@ const read = p => readFileSync(p, 'utf8');
const stages = ['Scope', 'Architecture', 'UI/UX', 'Planning', 'Delivery', 'UAT', 'Security', 'Releases'];
test('Strategy opens at the canonical autonomous lifecycle', () => {
assert.match(read('sidebars/strategy.ts'), /strategySidebar: \['target-operating-model', 'roadmap'/);
assert.match(read('sidebars/strategy.ts'), /strategySidebar:\s*\[\s*'target-operating-model',\s*'roadmap'/);
assert.match(read('docs/strategy/target-operating-model.mdx'), /^sidebar_label: Target$/m);
for (const id of ['target-operating-model']) {
const page = read(`docs/strategy/${id}.mdx`);
+111
View File
@@ -0,0 +1,111 @@
import assert from 'node:assert/strict';
import {readFileSync, readdirSync, mkdirSync, writeFileSync} from 'node:fs';
import {join} from 'node:path';
const normalize = path => path.replace(/\/$/, '');
const read = path => JSON.parse(readFileSync(path, 'utf8'));
// Use Docusaurus's resolved IDs/routes and sidebar trees, not filename guesses.
export function auditSidebars(instances, entrySidebars) {
const docs = new Map();
const menus = new Map();
for (const {plugin, version, documents, sidebars} of instances) {
const ids = new Set();
for (const doc of documents) {
assert(!ids.has(doc.id), `Duplicate document ID: ${plugin}:${version}:${doc.id}`);
ids.add(doc.id);
const route = normalize(doc.path);
assert(!docs.has(route), `Duplicate document route: ${route}`);
docs.set(route, {...doc, plugin, version, menu: `${plugin}:${version}:${doc.sidebar}`});
}
for (const [name, items] of Object.entries(sidebars)) menus.set(`${plugin}:${version}:${name}`, items);
}
const links = new Map();
for (const [menu, items] of menus) {
const found = new Set();
const walk = items => {
for (const item of items) {
assert(['category', 'link', 'html'].includes(item.type), `Unresolved sidebar type: ${item.type}`);
assert(!item.unlisted, `Hidden sidebar item: ${item.label}`);
if (item.href && !/^(https?:|mailto:)/.test(item.href)) {
const route = normalize(item.href);
if (item.docId) assert.equal(docs.get(route)?.id, item.docId, `Sidebar ID/route mismatch: ${item.docId}`);
assert(!found.has(route), `Duplicate sidebar route in ${menu}: ${route}`);
found.add(route);
}
if (item.type === 'category') walk(item.items);
}
};
walk(items);
links.set(menu, found);
}
const reachable = new Set(entrySidebars);
for (const menu of reachable) {
assert(menus.has(menu), `Unknown navigation sidebar: ${menu}`);
for (const route of links.get(menu)) {
const doc = docs.get(route);
if (doc) reachable.add(doc.menu);
}
}
for (const [route, doc] of docs) {
assert(reachable.has(doc.menu), `Unreachable sidebar for ${doc.plugin}:${doc.id}`);
assert(links.get(doc.menu)?.has(route), `Missing sidebar document: ${doc.plugin}:${doc.id}`);
}
return [...docs.entries()].map(([route, doc]) => ({route, id:doc.id, plugin:doc.plugin, sidebar:doc.sidebar}));
}
export async function verifySidebarCoverage() {
const global = read('.docusaurus/globalData.json')['docusaurus-plugin-content-docs'];
assert(global && Object.keys(global).length, 'No generated document inventory');
const instances = [];
for (const [plugin, data] of Object.entries(global)) {
const folder = `.docusaurus/docusaurus-plugin-content-docs/${plugin}/p`;
const resolved = readdirSync(folder).filter(f => f.endsWith('.json')).map(f => read(join(folder, f)).version);
for (const version of data.versions) {
const matches = resolved.filter(v => v.version === version.name);
assert.equal(matches.length, 1, `Missing/duplicate resolved version: ${plugin}:${version.name}`);
instances.push({plugin, version:version.name, documents:version.docs, sidebars:matches[0].docsSidebars});
}
}
const {default:config} = await import('../.docusaurus/docusaurus.config.mjs');
const entries = config.themeConfig.navbar.items.filter(i => i.type === 'docSidebar').map(i => `${i.docsPluginId || 'default'}:current:${i.sidebarId}`);
const documents = auditSidebars(instances, entries);
const routes = new Set(documents.map(d => `${d.route.replace(config.baseUrl, '')}/index.html`));
const exclusions = [];
const html = readdirSync('build', {recursive:true}).filter(f => f.endsWith('.html')).sort();
for (const file of html) {
if (routes.delete(file)) continue;
let reason;
if (file === 'index.html') reason = 'Dedicated React homepage, not a documentation page';
else if (file === '404.html') reason = 'Docusaurus not-found system page';
else if (file === 'search/index.html') reason = 'Local-search results UI, not a documentation page';
else if (file === 'strategy/index.html') {
assert(readFileSync(join('build',file),'utf8').includes('http-equiv="refresh"'), 'Strategy alias must remain a redirect');
reason = 'Legacy Strategy redirect alias to Target';
} else if (file.startsWith('diagrams/') && readFileSync(join('build',file),'utf8').includes('<svg')) {
assert.equal(readFileSync(join('build',file),'utf8'), readFileSync(join('static',file),'utf8'), `Viewer must match generated static asset: ${file}`);
reason = 'Standalone diagram viewer asset, not documentation';
}
assert(reason, `Unclassified published HTML route: ${file}`);
exclusions.push({file, reason});
}
assert.equal(routes.size, 0, `Missing published docs: ${[...routes]}`);
// Check all local links and anchors on every document, not only selected Strategy pages.
for (const {route} of documents) {
const file = `build/${route.replace(config.baseUrl, '')}/index.html`;
const body = readFileSync(file, 'utf8');
for (const [, href] of body.matchAll(/href="([^" ]+)"/g)) {
if (!href.startsWith(config.baseUrl) && !href.startsWith('#')) continue;
const url = new URL(href, `https://docs.invalid${route}/`);
let target = url.pathname.slice(config.baseUrl.length);
if (!target || target.endsWith('/')) target += 'index.html';
else if (!target.split('/').at(-1).includes('.')) target += '/index.html';
const targetBody = readFileSync(join('build', decodeURIComponent(target)), 'utf8');
if (url.hash) assert(targetBody.includes(`id="${decodeURIComponent(url.hash.slice(1))}"`), `Broken anchor: ${route} -> ${href}`);
}
}
mkdirSync('.cache', {recursive:true});
writeFileSync('.cache/sidebar-coverage.json', JSON.stringify({documents, exclusions}, null, 2));
console.log(`PASS: ${documents.length} published docs reachable in resolved sidebars; ${exclusions.length} classified non-doc HTML routes; all doc links/anchors checked`);
return {documents, exclusions};
}
+51
View File
@@ -0,0 +1,51 @@
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {auditSidebars} from './sidebar-coverage.mjs';
const fixture = () => [{plugin:'strategy', version:'current', documents:[
{id:'overview', path:'/strategy/overview/', sidebar:'main'},
{id:'nested/reference', path:'/strategy/reference/', sidebar:'main'},
], sidebars:{main:[{type:'category', label:'Stage', href:'/strategy/overview', items:[
{type:'category', label:'Reference', items:[{type:'link', docId:'nested/reference', href:'/strategy/reference'}]},
]}]}}];
const entries = ['strategy:current:main'];
test('resolved nested entries and linked category overviews both count', () => {
assert.equal(auditSidebars(fixture(), entries).length, 2);
});
test('missing docs fail even if Docusaurus assigns a displayed sidebar', () => {
const data = fixture();
data[0].sidebars.main[0].items = [];
assert.throws(() => auditSidebars(data, entries), /Missing sidebar document/);
});
test('new publishable docs and absent category overview links fail closed', () => {
const data = fixture();
data[0].documents.push({id:'new', path:'/new'});
assert.throws(() => auditSidebars(data, entries), /Unreachable sidebar/);
const withoutLink = fixture();
delete withoutLink[0].sidebars.main[0].href;
assert.throws(() => auditSidebars(withoutLink, entries), /Missing sidebar document/);
});
test('duplicate normalized routes and duplicate sidebar entries fail', () => {
const data = fixture();
data[0].documents.push({...data[0].documents[0], path:'/strategy/overview'});
assert.throws(() => auditSidebars(data, entries), /Duplicate document ID/);
data[0].documents.at(-1).id = 'different';
assert.throws(() => auditSidebars(data, entries), /Duplicate document route/);
const duplicate = fixture();
duplicate[0].sidebars.main.push({type:'link', href:'/strategy/overview/'});
assert.throws(() => auditSidebars(duplicate, entries), /Duplicate sidebar route/);
});
test('unlisted entries and unresolved sidebar types fail', () => {
const data = fixture();
data[0].sidebars.main[0].unlisted = true;
assert.throws(() => auditSidebars(data, entries), /Hidden sidebar item/);
data[0].sidebars.main[0] = {type:'autogenerated'};
assert.throws(() => auditSidebars(data, entries), /Unresolved sidebar type/);
});
test('disconnected plugin menus fail, cross-plugin sidebar links make them reachable', () => {
const data = fixture();
data.push({plugin:'default', version:'current', documents:[{id:'help', path:'/docs/help', sidebar:'docs'}], sidebars:{docs:[{type:'link', href:'/docs/help'}]}});
assert.throws(() => auditSidebars(data, entries), /Unreachable sidebar/);
data[0].sidebars.main.push({type:'link', href:'/docs/help/'});
assert.equal(auditSidebars(data, entries).length, 3);
});
+1 -1
View File
@@ -6,7 +6,7 @@ const read = path => readFileSync(path, 'utf8');
test('Skills Measurement is an open adoption collection plan, not a hidden catalogue', () => {
const page = read('docs/strategy/measurement.mdx');
assert.match(page, /title: Skills Measurement\nsidebar_label: Skills Measurement/);
assert.match(page, /title: Skills Measurement\nsidebar_label: Measurement/);
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('<details>'));
assert(page.includes('./reference/measurement-contracts.mdx'));
+60
View File
@@ -0,0 +1,60 @@
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync, mkdtempSync, rmSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {execFileSync} from 'node:child_process';
import {skillsViewer,isSkillsStory} from './skills-viewer.mjs';
const read = p => readFileSync(p,'utf8');
const topics = ['governance','quality','adoption','measurement','maintenance'];
test('all six Skills Adoption pages account for exactly five diagrams and ten sources',()=>{
let count=0;
for(const route of ['stage-01-skills-adoption',...topics.map(t=>t==='measurement'?t:`skills-${t}`)]){
const diagrams=[...read(`docs/strategy/${route}.mdx`).matchAll(/<ArchifyDiagram name="([^"]+)"/g)];
assert.equal(diagrams.length,route.startsWith('stage-')?0: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,5);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`));
const mobile=JSON.parse(read(`diagrams/${name}-mobile.architecture.json`));
const semantic = d=>d.components.map(({pos,size,...n})=>n);
assert.deepEqual(semantic(desktop),semantic(mobile));
assert(new Set(desktop.components.map(n=>n.pos[0])).size>1);
assert.equal(new Set(mobile.components.map(n=>n.pos[0])).size,1);
for(const n of desktop.components){assert(n.tag);assert(n.sublabel);assert(n.sublabel.split('\n').length<=4);}
const dir=mkdtempSync(join(tmpdir(),'skills-viewer-'));
try{
const path=join(dir,'native.html');
execFileSync(process.execPath,['vendor/archify/renderers/architecture/render-architecture.mjs',`diagrams/${name}.architecture.json`,path]);
const native=read(path);const html=skillsViewer(native,desktop);
assert.equal((html.match(/<g\b[^>]*data-node-id=/g)||[]).length,desktop.components.length);
assert.equal((html.match(/<text data-node-label=/g)||[]).length,0); // Adapter uses explicit position before semantic attributes.
assert.equal((html.match(/data-node-label=""/g)||[]).length,desktop.components.length);
assert(html.includes('eldorado-skills-typography'));
assert.throws(()=>skillsViewer(native.replace('data-node-id="'+desktop.components[0].id+'"','data-node-id="missing"'),desktop));
const bad=structuredClone(desktop);bad.components[0].sublabel='';assert.throws(()=>skillsViewer(native,bad));
}finally{rmSync(dir,{recursive:true,force:true});}
});
test('domain contracts retain human authority, honest measures and Delivery-first rollout',()=>{
const text=t=>JSON.stringify(JSON.parse(read(`diagrams/strategy-${t}.architecture.json`)));
assert(text('governance').includes('Author is not sole approver'));
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 ['Tracker + acceptance','Invoices + review minutes','Changes + defects','Anonymous 1–5 survey','Missing buckets = partial','Zero denominator = N/A'])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',()=>{
const spec=t=>JSON.parse(read(`diagrams/strategy-${t}.architecture.json`));
const has=(d,a,b)=>assert(d.connections.some(e=>e.from===a&&e.to===b),`${a} → ${b}`);
const g=spec('governance');const steps=g.components.filter(n=>/^0[1-6]/.test(n.label));
assert.equal(steps.length,6);
for(let i=1;i<steps.length;i++){assert(steps[i].pos[0]>steps[i-1].pos[0]);assert(steps[i].pos[1]>steps[i-1].pos[1]);has(g,steps[i-1].id,steps[i].id);}
has(g,'approve','owner');has(g,'distribute','withdraw');
const q=spec('quality');for(const id of ['selection','safety','artifact','outcome'])has(q,id,'gate');has(q,'gate','accept');has(q,'gate','rework');
const a=spec('adoption');for(const [x,y] of [['human','agent'],['agent','human'],['agent','learn'],['learn','delivery'],['learn','expand'],['human','expand']])has(a,x,y);
const m=spec('measurement');for(const id of ['success','quality','cost','confidence']){has(m,id+'-source',id);has(m,id,'interpret');}
const c=spec('maintenance');for(const [x,y] of [['observe','triage'],['triage','change'],['change','validate'],['validate','release'],['release','observe'],['triage','safe'],['safe','observe'],['triage','retire'],['triage','contain'],['contain','change']])has(c,x,y);
});
+44
View File
@@ -0,0 +1,44 @@
// Native editable geometry and edges; scoped editorial typography for Stage 01.
export const isSkillsStory = name => /^strategy-(governance|quality|adoption|measurement|maintenance)(-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;
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);
if (!match) throw new Error(`Missing Skills node: ${node.id}`);
const original = match[0];
const rectangles = [...original.matchAll(/<rect\b[^>]*class="c-[^"]+"[^>]*\/>/g)];
if (rectangles.length !== 2) throw new Error('Skills shape contract drift');
const [x,y] = node.pos;
const lines = node.sublabel.split('\n');
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"')
+ 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>');
}
const roots = [...html.matchAll(/<svg\b[^>]*role="img"[^>]*>/g)];
if (roots.length !== 1) throw new Error('Skills SVG contract drift');
const [width,height] = spec.meta.viewBox;
const root = roots[0][0].replace(/viewBox="[^"]*"/,`viewBox="0 0 ${width} ${height}"`);
// Embedded in SVG so downloaded files retain typography without the viewer.
html = html.replace(roots[0][0],root+`<style id="eldorado-skills-typography">
[data-node-id] text {font-family:Arial,sans-serif;}
rect[fill="url(#grid)"] {opacity:0;}
[data-node-id] > rect:not(.c-mask) {stroke-width:3;}
[data-edge-id] {opacity:1;}
</style>`);
// Native labels use tiny technical annotations. Enlarge both text and mask,
// retaining the native edge identity, routing and explicit label position.
html = html.replace(/(<g data-detail="context"[^>]*data-edge-id[^>]*>)([\s\S]*?)(<\/g>)/g, (all,open,body,close) => {
const text = body.match(/<text x="([^"]+)" y="([^"]+)"([^>]*)>([\s\S]*?)<\/text>/);
if (!text) throw new Error('Skills edge label contract drift');
const x=Number(text[1]), y=Number(text[2]), width=text[4].replace(/&[^;]+;/g,'x').length*17+24;
return open+`<rect x="${x-width/2}" y="${y-34}" width="${width}" height="44" class="c-mask"/>`+text[0].replace('font-size="8"',`font-size="${detailSize}"`)+close;
});
return html;
}
+35 -5
View File
@@ -1,17 +1,47 @@
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync, existsSync} from 'node:fs';
import {runInNewContext} from 'node:vm';
import ts from 'typescript';
const read = p => readFileSync(p, 'utf8');
test('Strategy has one canonical Target, then Roadmap, without a landing menu duplicate', () => {
const sidebar = read('sidebars/strategy.ts').split('strategySidebar: [')[1].split(']')[0];
const ids = [...sidebar.matchAll(/'([^']+)'/g)].map(match => match[1]);
assert.deepEqual(ids, ['target-operating-model', 'roadmap', 'skills-governance', 'skills-quality', 'skills-adoption', 'measurement', 'skills-maintenance']);
assert.equal(ids.filter(id => id === 'target-operating-model').length, 1);
test('Strategy has Target, Roadmap, a linked Skills Adoption category and Autonomous Agent', () => {
const compiled = ts.transpileModule(read('sidebars/strategy.ts'), {compilerOptions:{module:ts.ModuleKind.CommonJS}}).outputText;
const sandbox = {exports:{}};
runInNewContext(compiled, sandbox);
const sidebar = JSON.parse(JSON.stringify(sandbox.exports.default.strategySidebar));
assert.deepEqual(sidebar, ['target-operating-model', 'roadmap', {
type:'category', label:'Stage - 01 - Skills Adoption',
link:{type:'doc', id:'stage-01-skills-adoption'}, collapsed:true,
items:[
{type:'doc', id:'skills-governance', label:'Governance'},
{type:'doc', id:'skills-quality', label:'Quality'},
{type:'doc', id:'skills-adoption', label:'Adoption'},
{type:'doc', id:'measurement', label:'Measurement'},
{type:'doc', id:'skills-maintenance', label:'Maintenance'},
{type:'category', label:'Reference', collapsed:true, items:[{type:'doc', id:'reference/measurement-contracts', label:'Measurement Contracts'}]},
],
}, 'stage-02-autonomous-agent']);
assert.match(read('docs/strategy/target-operating-model.mdx'), /^sidebar_label: Target$/m);
assert.match(read('docusaurus.config.ts'), /type: 'docSidebar', docsPluginId: 'strategy', sidebarId: 'strategySidebar', label: 'Strategy'/);
});
test('stage overviews explain the foundation and future without replacing topic content', () => {
const first = read('docs/strategy/stage-01-skills-adoption.mdx');
const second = read('docs/strategy/stage-02-autonomous-agent.mdx');
for (const [id, title, page] of [
['stage-01-skills-adoption', 'Stage - 01 - Skills Adoption', first],
['stage-02-autonomous-agent', 'Stage - 02 - Autonomous Agent', second],
]) {
assert(page.includes(`id: ${id}\ntitle: ${title}`));
for (const link of ['./roadmap.mdx', './target-operating-model.mdx']) assert(page.includes(link));
}
for (const id of ['skills-governance','skills-quality','skills-adoption','measurement','skills-maintenance']) assert(first.includes(`./${id}.mdx`));
for (const marker of ['human + AI Agent','Delivery first','order is not yet decided','KPIs','baseline','topology','feedback','new current situation']) assert(first.includes(marker), marker);
for (const marker of ['future','proven skills','measurements','working practices','direction and review','not deployed autonomy','approved KPI thresholds']) assert(second.includes(marker), marker);
assert.match(read('docs/strategy/measurement.mdx'), /^title: Skills Measurement\nsidebar_label: Measurement$/m);
});
test('old Strategy entry redirects to Target instead of maintaining a second document', () => {
assert(!existsSync('docs/strategy/index.mdx'));
assert.match(read('docusaurus.config.ts'), /'@docusaurus\/plugin-client-redirects'/);
+2 -2
View File
@@ -35,10 +35,10 @@ test('mobile story diagrams preserve the same nodes and directed relationships',
}
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 <= (name === 'strategy-target' ? 18 : 7), name);
assert(desktop.components.length >= 5, name);
const semantic = d => ({nodes:d.components.map(({pos,size,...n})=>n),edges:d.connections.map(({fromSide,toSide,route,via,labelAt,...e})=>e)});
assert.deepEqual(semantic(desktop), semantic(mobile), name);
assert(desktop.connections.every(e => (e.label || e.id.startsWith('govern-')) && e.from !== e.to), name);
assert(desktop.connections.every(e => (e.label || e.id.startsWith('govern-') || /^strategy-(governance|quality|adoption|measurement|maintenance)$/.test(name)) && e.from !== e.to), name);
assert.equal(desktop.meta.legend.mode, 'hidden');
}
});
+3 -2
View File
@@ -3,9 +3,10 @@ import assert from 'node:assert/strict';
import {readFileSync, readdirSync} from 'node:fs';
const read = p => readFileSync(p, 'utf8');
const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
test('seven canonical strategy pages preserve target boundary and separate evidence', () => {
test('seven existing strategy pages and two stage overviews preserve canonical routes', () => {
assert.equal(model.pages.length, 7);
assert.equal(readdirSync('docs/strategy').filter(p => p.endsWith('.mdx')).length, 7);
assert.deepEqual(readdirSync('docs/strategy').filter(p => p.endsWith('.mdx')).sort(),
[...model.pages.map(({id}) => `${id}.mdx`), 'stage-01-skills-adoption.mdx', 'stage-02-autonomous-agent.mdx'].sort());
for (const {id} of model.pages) {
const page = read(`docs/strategy/${id}.mdx`);
assert(page.includes(`id: ${id}`));
+9 -2
View File
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict';
import {readFileSync, readdirSync, existsSync} from 'node:fs';
import {join} from 'node:path';
import {verifySidebarCoverage} from './sidebar-coverage.mjs';
const read = p => readFileSync(p, 'utf8');
const config = read('docusaurus.config.ts');
assert(config.includes("baseUrl: '/eldorado-v1/documentation/'"));
@@ -16,15 +17,21 @@ const workflow = read('.gitea/workflows/pages.yml');
assert(workflow.includes('mountpoint -q'));
assert(workflow.includes("gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'"));
if (process.argv.includes('--build')) {
await verifySidebarCoverage();
const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
const indexes = readdirSync('build').filter(f => /^search-index.*\.json$/.test(f)).map(f => read(join('build',f))).join('\n');
const redirect = read('build/strategy/index.html');
assert(redirect.includes('/eldorado-v1/documentation/strategy/target-operating-model/'));
assert(redirect.includes('window.location.href') && redirect.includes('http-equiv="refresh"') && !redirect.includes('archify-panel'), 'Landing must redirect, not duplicate Target');
for (const {id, title} of model.pages) {
const strategyPages = [...model.pages,
{id:'stage-01-skills-adoption', title:'Stage - 01 - Skills Adoption'},
{id:'stage-02-autonomous-agent', title:'Stage - 02 - Autonomous Agent'},
];
for (const {id, title} of strategyPages) {
const route = `strategy/${id === 'index' ? '' : `${id}/`}index.html`;
const page = read(join('build',route));
assert(page.includes(title) && page.includes('Target operating model'), route);
assert(page.includes(title), route);
if (!id.startsWith('stage-')) assert(page.includes('Target operating model'), route);
assert(indexes.includes(title), `Missing indexed Strategy page: ${id}`);
for (const match of page.matchAll(/href="(\/eldorado-v1\/documentation\/[^"?#]*)/g)) {
const local = decodeURIComponent(match[1].replace('/eldorado-v1/documentation/',''));
+10 -1
View File
@@ -1,2 +1,11 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
export default {contributionSidebar: ['contribute', 'diagrams']} satisfies SidebarsConfig;
export default {
contributionSidebar: ['contribute', 'diagrams', {
type: 'category', label: 'Portal maintenance', collapsed: true,
items: [
{type: 'link', label: 'Starting point', href: '/eldorado-v1/documentation/docs/overview/'},
{type: 'link', label: 'Update and publish', href: '/eldorado-v1/documentation/docs/maintain/'},
{type: 'link', label: 'Site configuration', href: '/eldorado-v1/documentation/docs/reference/'},
],
}],
} satisfies SidebarsConfig;
+24 -1
View File
@@ -1,2 +1,25 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
export default {strategySidebar: ['target-operating-model', 'roadmap', 'skills-governance', 'skills-quality', 'skills-adoption', 'measurement', 'skills-maintenance']} satisfies SidebarsConfig;
export default {
strategySidebar: [
'target-operating-model',
'roadmap',
{
type: 'category',
label: 'Stage - 01 - Skills Adoption',
link: {type: 'doc', id: 'stage-01-skills-adoption'},
collapsed: true,
items: [
{type: 'doc', id: 'skills-governance', label: 'Governance'},
{type: 'doc', id: 'skills-quality', label: 'Quality'},
{type: 'doc', id: 'skills-adoption', label: 'Adoption'},
{type: 'doc', id: 'measurement', label: 'Measurement'},
{type: 'doc', id: 'skills-maintenance', label: 'Maintenance'},
{
type: 'category', label: 'Reference', collapsed: true,
items: [{type: 'doc', id: 'reference/measurement-contracts', label: 'Measurement Contracts'}],
},
],
},
'stage-02-autonomous-agent',
],
} satisfies SidebarsConfig;
+6 -5
View File
@@ -12,14 +12,15 @@ export default function ArchifyDiagram({name, title, compact = false, story = fa
const headingId = useId();
const [expanded, setExpanded] = useState(false);
const [narrowRoadmap, setNarrowRoadmap] = useState(false);
const skillsStory = /^strategy-(governance|quality|adoption|measurement|maintenance)$/.test(name);
useEffect(() => {
if (!name.startsWith('strategy-roadmap')) return;
const media = window.matchMedia('(max-width: 999px)');
if (!name.startsWith('strategy-roadmap') && !skillsStory) return;
const media = window.matchMedia(skillsStory ? '(max-width: 1100px)' : '(max-width: 999px)');
const update = () => setNarrowRoadmap(media.matches);
update();
media.addEventListener('change', update);
return () => media.removeEventListener('change', update);
}, [name]);
}, [name, skillsStory]);
const {colorMode, setColorMode} = useColorMode();
const initialTheme = useRef(colorMode);
const desktop = useRef<HTMLIFrameElement>(null);
@@ -66,7 +67,7 @@ export default function ArchifyDiagram({name, title, compact = false, story = fa
return () => {document.body.style.overflow = previousOverflow;};
}, [expanded]);
const close = () => {dialog.current?.close();};
return <section className={`archify-panel${compact ? ' archify-panel--compact' : ''}${story ? ' archify-panel--story' : ''}`} data-diagram={name} aria-label={title}>
return <section className={`archify-panel${compact ? ' archify-panel--compact' : ''}${story ? ' archify-panel--story' : ''}${skillsStory ? ' archify-panel--skills' : ''}`} data-diagram={name} aria-label={title}>
<div className="archify-toolbar"><button ref={opener} type="button" className="archify-expand" aria-label={`Expand ${title}`} title="Expand diagram" onClick={() => setExpanded(true)}><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true"><path d="M8 3H3v5M16 3h5v5M21 16v5h-5M8 21H3v-5" /></svg></button></div>
{story ? <>
<iframe className="story-desktop" ref={desktop} onLoad={() => syncTheme(desktop.current)} src={`${source}?embed=1&theme=${initialTheme.current}`} title={title} sandbox="allow-scripts allow-downloads" loading="lazy" referrerPolicy="no-referrer" />
@@ -74,7 +75,7 @@ export default function ArchifyDiagram({name, title, compact = false, story = fa
</> : <iframe ref={desktop} onLoad={() => syncTheme(desktop.current)} src={`${source}?theme=${initialTheme.current}`} title={title} sandbox="allow-scripts allow-downloads" loading="lazy" referrerPolicy="no-referrer" />}
<dialog ref={dialog} className="archify-modal" aria-labelledby={headingId} onClose={() => {setExpanded(false); opener.current?.focus();}}>
<div className="archify-modal-toolbar"><strong id={headingId}>{title}</strong><button type="button" onClick={close} autoFocus aria-label="Close full-screen diagram"><span aria-hidden="true">×</span></button></div>
{expanded && <iframe ref={fullscreen} onLoad={() => syncTheme(fullscreen.current)} src={`${narrowRoadmap ? mobileSource : source}?${name.startsWith('strategy-roadmap') ? 'embed=1&' : ''}theme=${initialTheme.current}`} title={`${title} — expanded`} sandbox="allow-scripts allow-downloads" referrerPolicy="no-referrer" />}
{expanded && <iframe ref={fullscreen} onLoad={() => syncTheme(fullscreen.current)} src={`${narrowRoadmap ? mobileSource : source}?${name.startsWith('strategy-roadmap') || skillsStory ? 'embed=1&' : ''}theme=${initialTheme.current}`} title={`${title} — expanded`} sandbox="allow-scripts allow-downloads" referrerPolicy="no-referrer" />}
</dialog>
</section>;
}
+19 -4
View File
@@ -116,8 +116,7 @@ a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offse
.archify-panel--story[data-diagram="strategy-roadmap-loop"] {--roadmap-height: 1840px;}
.archify-panel--story[data-diagram="strategy-roadmap-loop"] > .story-desktop {height: 460px;}
.archify-panel--story[data-diagram="strategy-target"] > iframe {aspect-ratio: 1620 / 1430;}
.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);}
.strategy-content summary {cursor: pointer; font-weight: 700;}
.strategy-content summary:focus-visible {outline: 3px solid #b88719; outline-offset: 4px;}
@@ -127,8 +126,7 @@ a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offse
.archify-panel--story > .story-desktop {display: none;}
.archify-panel--story > .story-mobile {display: block; height: auto; aspect-ratio: 240 / 748;}
.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: 680 / 2340;}
}
/* Keep the three-card loop readable beside the documentation sidebar. */
@@ -152,3 +150,20 @@ a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offse
[data-diagram^="strategy-roadmap"] .archify-modal iframe {flex: none; height: var(--roadmap-height); min-height: var(--roadmap-height);}
}
@media (prefers-reduced-motion: reduce) {.start-card {transition: none;} .start-card:hover {transform: none;}}
/* Skills Adoption alone uses the full reading column, not a narrow preview. */
.theme-doc-markdown:has(.archify-panel--skills) {max-width: none;}
.archify-panel--story.archify-panel--skills {max-width: none;}
.archify-panel--skills {--skills-wide: 2000 / 1200; --skills-narrow: 880 / 2900;}
.archify-panel--skills[data-diagram="strategy-governance"] {--skills-wide: 1760 / 1550;}
.archify-panel--skills[data-diagram="strategy-adoption"] {--skills-wide: 2040 / 1300; --skills-narrow: 880 / 2200;}
.archify-panel--skills[data-diagram="strategy-measurement"] {--skills-wide: 2400 / 1280; --skills-narrow: 880 / 3900;}
.archify-panel--skills[data-diagram="strategy-maintenance"] {--skills-wide: 2630 / 1480; --skills-narrow: 880 / 3250;}
.archify-panel--story.archify-panel--skills > .story-desktop {aspect-ratio: var(--skills-wide); height: auto;}
@media (max-width: 1100px) {
.archify-panel--story.archify-panel--skills > .story-desktop {display: none;}
.archify-panel--story.archify-panel--skills > .story-mobile {display: block; aspect-ratio: var(--skills-narrow); height: auto; max-width: 440px; margin-inline: auto;}
.archify-panel--skills .archify-modal {overflow-y: auto; overscroll-behavior: contain;}
.archify-panel--skills .archify-modal-toolbar {position: sticky; top: 0; z-index: 1; flex-shrink: 0; background: var(--surface);}
.archify-panel--skills .archify-modal iframe {flex: none; height: auto; min-height: 0; aspect-ratio: var(--skills-narrow); max-width: 600px; margin-inline: auto;}
}