Compare commits

...
Author SHA1 Message Date
jarvis-at-skic abe00994a1 docs: [verified] separate Measurement registry and workflow Baseline
Build and publish Eldorado documentation / build (pull_request) Successful in 46s
2026-10-07 13:33:07 +00:00
jarvis-at-skic c4b4c38e62 Merge pull request 'docs: separate metric pages under Measurement' (#34) from feat/measurement-metric-pages into main
Build and publish Eldorado documentation / build (push) Successful in 41s
2026-10-06 16:26:44 -07:00
jarvis-at-skic 85852f0744 docs: [verified] move metric contracts under Measurement
Build and publish Eldorado documentation / build (pull_request) Successful in 41s
2026-10-06 23:25:30 +00:00
jarvis-at-skic b9c2047c61 Merge pull request 'feat: art-direct five Skills Adoption stories' (#33) from feat/skills-topic-compositions into main
Build and publish Eldorado documentation / build (push) Successful in 39s
2026-10-06 15:37:16 -07:00
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
66 changed files with 3989 additions and 1365 deletions

No files matched your search

@@ -7,140 +7,239 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
880,
2040
]
}, },
"components": [ "components": [
{ {
"id": "delivery", "id": "delivery",
"type": "backend", "type": "external",
"label": "Delivery first", "label": "Delivery first",
"tag": "Understand real work", "tag": "Start with a real workflow",
"sublabel": "Codebase + principles\nSystem logic and tests\nBuilds and packaging\nDeployments + baseline", "sublabel": "Workflow baseline + canary",
"pos": [ "pos": [
40, 60,
40 120
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "workflow", "id": "human",
"type": "security", "type": "backend",
"label": "Choose bounded work", "label": "Human",
"tag": "Team + Product owner", "tag": "Direct + guide + review",
"sublabel": "Define accepted outcome\nData & AI shapes the skill\nQuality sets acceptance\nSecurity clears data", "sublabel": "Define outcome; inspect artifacts",
"pos": [ "pos": [
40, 60,
610 1280
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "coach", "id": "agent",
"type": "frontend", "type": "external",
"label": "Practice together", "label": "AI Agent",
"tag": "Human + AI Agent", "tag": "Supported client + approved skills",
"sublabel": "Platform qualifies client\nPeople supports learning\nSelect skill; inspect work\nKeep a manual path", "sublabel": "Produce work within the boundary",
"pos": [ "pos": [
40, 60,
1180 910
], ],
"size": [ "size": [
560, 740,
440 220
]
},
{
"id": "verify",
"type": "security",
"label": "Accept actual work",
"tag": "Independent reviewer",
"sublabel": "Check artifacts, not calls\nRecognize unsafe inputs\nUse incident routes\nExplain limits + recovery",
"pos": [
40,
1750
], ],
"size": [ "icon": "none"
560,
440
]
}, },
{ {
"id": "learn", "id": "learn",
"type": "database", "type": "external",
"label": "Review the feedback", "label": "Learn from real work",
"tag": "Owning team", "tag": "Ongoing, reviewed improvement",
"sublabel": "Collect metrics + friction\nNon-use is useful feedback\nRevise through review\nNo quotas or rankings", "sublabel": "Feedback + selected measures\nRetain a manual path\nNo quotas or rankings",
"pos": [ "pos": [
40, 60,
2320 490
], ],
"size": [ "size": [
560, 740,
440 270
] ],
"icon": "none"
}, },
{ {
"id": "expand", "id": "expand",
"type": "cloud", "type": "backend",
"label": "Expand with evidence", "label": "Expand on evidence",
"tag": "Other SDLC phases", "tag": "Other SDLC phases",
"sublabel": "Later order undecided\nRepeat baseline + topology\nDelivery keeps improving\nNot autonomous authority", "sublabel": "Later order undecided\nDelivery keeps improving",
"pos": [ "pos": [
40, 60,
2890 1650
], ],
"size": [ "size": [
560, 740,
440 240
] ],
"icon": "none"
} }
], ],
"connections": [ "connections": [
{ {
"id": "flow-0", "id": "delivery-agent",
"from": "delivery", "from": "delivery",
"to": "workflow", "to": "agent",
"fromSide": "bottom", "label": "Practice",
"toSide": "top", "fromSide": "right",
"route": "straight" "toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
230.0
],
[
850,
1020.0
]
],
"labelAt": [
780,
870
]
}, },
{ {
"id": "flow-1", "id": "human-agent",
"from": "workflow", "from": "human",
"to": "coach", "to": "agent",
"fromSide": "bottom", "label": "Direction",
"toSide": "top", "fromSide": "right",
"route": "straight" "toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
824,
1390.0
],
[
824,
1020.0
]
],
"labelAt": [
770,
1240
]
}, },
{ {
"id": "flow-2", "id": "agent-human",
"from": "coach", "from": "agent",
"to": "verify", "to": "human",
"label": "Work to inspect",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1185
]
}, },
{ {
"id": "flow-3", "id": "agent-learn",
"from": "verify", "from": "agent",
"to": "learn", "to": "learn",
"fromSide": "bottom", "label": "Observe",
"toSide": "top", "fromSide": "top",
"route": "straight" "toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
815
]
}, },
{ {
"id": "flow-4", "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", "from": "learn",
"to": "expand", "to": "expand",
"fromSide": "bottom", "label": "Feedback",
"toSide": "top", "fromSide": "left",
"route": "straight" "toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
20,
625.0
],
[
20,
1770.0
]
],
"labelAt": [
620,
1610
]
} }
] ]
} }
+180 -83
View File
@@ -7,140 +7,237 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
2040,
1160
]
}, },
"components": [ "components": [
{ {
"id": "delivery", "id": "delivery",
"type": "backend", "type": "external",
"label": "Delivery first", "label": "Delivery first",
"tag": "Understand real work", "tag": "Start with a real workflow",
"sublabel": "Codebase + principles\nSystem logic and tests\nBuilds and packaging\nDeployments + baseline", "sublabel": "Workflow baseline + canary",
"pos": [ "pos": [
40, 60,
40 350
], ],
"size": [ "size": [
560, 470,
440 210
] ],
"icon": "none"
}, },
{ {
"id": "workflow", "id": "human",
"type": "security", "type": "backend",
"label": "Choose bounded work", "label": "Human",
"tag": "Team + Product owner", "tag": "Direct + guide + review",
"sublabel": "Define accepted outcome\nData & AI shapes the skill\nQuality sets acceptance\nSecurity clears data", "sublabel": "Define outcome; inspect artifacts",
"pos": [ "pos": [
700, 700,
40 90
], ],
"size": [ "size": [
560, 570,
440 190
] ],
"icon": "none"
}, },
{ {
"id": "coach", "id": "agent",
"type": "frontend", "type": "external",
"label": "Practice together", "label": "AI Agent",
"tag": "Human + AI Agent", "tag": "Supported client + approved skills",
"sublabel": "Platform qualifies client\nPeople supports learning\nSelect skill; inspect work\nKeep a manual path", "sublabel": "Produce work within the boundary",
"pos": [ "pos": [
1360, 700,
40 440
], ],
"size": [ "size": [
560, 570,
440 190
]
},
{
"id": "verify",
"type": "security",
"label": "Accept actual work",
"tag": "Independent reviewer",
"sublabel": "Check artifacts, not calls\nRecognize unsafe inputs\nUse incident routes\nExplain limits + recovery",
"pos": [
1360,
610
], ],
"size": [ "icon": "none"
560,
440
]
}, },
{ {
"id": "learn", "id": "learn",
"type": "database", "type": "external",
"label": "Review the feedback", "label": "Learn from real work",
"tag": "Owning team", "tag": "Ongoing, reviewed improvement",
"sublabel": "Collect metrics + friction\nNon-use is useful feedback\nRevise through review\nNo quotas or rankings", "sublabel": "Feedback + selected measures\nRetain a manual path\nNo quotas or rankings",
"pos": [ "pos": [
700, 700,
610 800
], ],
"size": [ "size": [
560, 570,
440 270
] ],
"icon": "none"
}, },
{ {
"id": "expand", "id": "expand",
"type": "cloud", "type": "backend",
"label": "Expand with evidence", "label": "Expand on evidence",
"tag": "Other SDLC phases", "tag": "Other SDLC phases",
"sublabel": "Later order undecided\nRepeat baseline + topology\nDelivery keeps improving\nNot autonomous authority", "sublabel": "Later order undecided\nDelivery keeps improving",
"pos": [ "pos": [
40, 1460,
610 360
], ],
"size": [ "size": [
560, 520,
440 240
] ],
"icon": "none"
} }
], ],
"connections": [ "connections": [
{ {
"id": "flow-0", "id": "delivery-agent",
"from": "delivery", "from": "delivery",
"to": "workflow", "to": "agent",
"label": "Practice",
"fromSide": "right", "fromSide": "right",
"toSide": "left", "toSide": "left",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"via": [
[
585,
455
],
[
585,
535
]
],
"labelAt": [
570,
600
]
}, },
{ {
"id": "flow-1", "id": "human-agent",
"from": "workflow", "from": "human",
"to": "coach", "to": "agent",
"fromSide": "right", "label": "Direction",
"toSide": "left",
"route": "straight"
},
{
"id": "flow-2",
"from": "coach",
"to": "verify",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
1100,
360
]
}, },
{ {
"id": "flow-3", "id": "agent-human",
"from": "verify", "from": "agent",
"to": "learn", "to": "human",
"label": "Work to inspect",
"fromSide": "left", "fromSide": "left",
"toSide": "right", "toSide": "left",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"via": [
[
630,
535
],
[
630,
185
]
],
"labelAt": [
470,
240
]
}, },
{ {
"id": "flow-4", "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", "from": "learn",
"to": "expand", "to": "expand",
"fromSide": "left", "label": "Feedback",
"toSide": "right", "fromSide": "right",
"route": "straight" "toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1720,
935
]
],
"labelAt": [
1480,
895
]
} }
] ]
} }
@@ -0,0 +1,144 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Workflow baseline",
"output": "static/diagrams/strategy-baseline-mobile/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
},
"viewBox": [
880,
2050
]
},
"components": [
{
"id": "workflow",
"type": "external",
"label": "Current workflow",
"tag": "01 / Understand",
"sublabel": "Task, scope, people\nTools + handoffs\nCurrent practice",
"pos": [
90,
50
],
"size": [
700,
350
],
"icon": "none"
},
{
"id": "selection",
"type": "frontend",
"label": "Relevant selection",
"tag": "02 / Choose",
"sublabel": "Registry candidates\nReasons for inclusion\nDefer what does not fit",
"pos": [
90,
450
],
"size": [
700,
350
],
"icon": "none"
},
{
"id": "evidence",
"type": "frontend",
"label": "Feasible evidence",
"tag": "03 / Agree",
"sublabel": "Sources + period\nPrivacy, access, owner\nTest joins + coverage",
"pos": [
90,
850
],
"size": [
700,
350
],
"icon": "none"
},
{
"id": "position",
"type": "backend",
"label": "Starting position",
"tag": "04 / Record",
"sublabel": "Actual values + limits\nOr unavailable gaps\nNo invented baseline",
"pos": [
90,
1250
],
"size": [
700,
350
],
"icon": "none"
},
{
"id": "comparison",
"type": "backend",
"label": "Later comparison",
"tag": "05 / Evaluate",
"sublabel": "Comparable rules\nContext + uncertainty\nNot causal ROI",
"pos": [
90,
1650
],
"size": [
700,
350
],
"icon": "none"
}
],
"connections": [
{
"id": "workflow-selection",
"from": "workflow",
"to": "selection",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "selection-evidence",
"from": "selection",
"to": "evidence",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "evidence-position",
"from": "evidence",
"to": "position",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default"
},
{
"id": "position-comparison",
"from": "position",
"to": "comparison",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default"
}
]
}
@@ -0,0 +1,184 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Workflow baseline",
"output": "static/diagrams/strategy-baseline/index.html",
"quality_profile": "showcase",
"legend": {
"mode": "hidden"
},
"viewBox": [
2400,
1120
]
},
"components": [
{
"id": "workflow",
"type": "external",
"label": "Current workflow",
"tag": "01 / Understand",
"sublabel": "Task, scope, people\nTools + handoffs\nCurrent practice",
"pos": [
40,
55
],
"size": [
440,
290
],
"icon": "none"
},
{
"id": "selection",
"type": "frontend",
"label": "Relevant selection",
"tag": "02 / Choose",
"sublabel": "Registry candidates\nReasons for inclusion\nDefer what does not fit",
"pos": [
510,
235
],
"size": [
440,
290
],
"icon": "none"
},
{
"id": "evidence",
"type": "frontend",
"label": "Feasible evidence",
"tag": "03 / Agree",
"sublabel": "Sources + period\nPrivacy, access, owner\nTest joins + coverage",
"pos": [
980,
415
],
"size": [
440,
290
],
"icon": "none"
},
{
"id": "position",
"type": "backend",
"label": "Starting position",
"tag": "04 / Record",
"sublabel": "Actual values + limits\nOr unavailable gaps\nNo invented baseline",
"pos": [
1450,
595
],
"size": [
440,
290
],
"icon": "none"
},
{
"id": "comparison",
"type": "backend",
"label": "Later comparison",
"tag": "05 / Evaluate",
"sublabel": "Comparable rules\nContext + uncertainty\nNot causal ROI",
"pos": [
1920,
775
],
"size": [
440,
290
],
"icon": "none"
}
],
"connections": [
{
"id": "workflow-selection",
"from": "workflow",
"to": "selection",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
495.0,
200.0
],
[
495.0,
380.0
]
]
},
{
"id": "selection-evidence",
"from": "selection",
"to": "evidence",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
965.0,
380.0
],
[
965.0,
560.0
]
]
},
{
"id": "evidence-position",
"from": "evidence",
"to": "position",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1435.0,
560.0
],
[
1435.0,
740.0
]
]
},
{
"id": "position-comparison",
"from": "position",
"to": "comparison",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1905.0,
740.0
],
[
1905.0,
920.0
]
]
}
]
}
@@ -7,140 +7,241 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
880,
2710
]
}, },
"components": [ "components": [
{ {
"id": "owner", "id": "owner",
"type": "backend", "type": "external",
"label": "Propose a skill", "label": "01 Propose",
"tag": "Owning team", "tag": "Owning team",
"sublabel": "Problem + intended users\nData and authority scope\nSKILL.md + dependencies\nTestable outcome", "sublabel": "Scope + testable outcome",
"pos": [ "pos": [
40, 60,
40 120
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "review", "id": "review",
"type": "security", "type": "external",
"label": "Independent review", "label": "02 Review independently",
"tag": "Quality + Security", "tag": "Quality + Security",
"sublabel": "Evaluate useful outcomes\nCheck risk boundaries\nAuthor is not sole approver\nRecord revision evidence", "sublabel": "Author is not sole approver",
"pos": [ "pos": [
40, 60,
610 490
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "canary", "id": "canary",
"type": "frontend", "type": "external",
"label": "Qualify real use", "label": "03 Qualify real use",
"tag": "Human + AI Agent", "tag": "Human + AI Agent",
"sublabel": "Test exact client build\nRun a bounded canary\nInspect work independently\nNo failed mandatory gate", "sublabel": "Exact client + bounded canary",
"pos": [ "pos": [
40, 60,
1180 860
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "approve", "id": "approve",
"type": "security", "type": "backend",
"label": "Approve exact scope", "label": "04 Approve exact scope",
"tag": "Accountable approvers", "tag": "Accountable approvers",
"sublabel": "Bind approval to digest\nPermitted uses + expiry\nExceptions cannot bypass\nprotected controls", "sublabel": "Digest + permitted uses + expiry",
"pos": [ "pos": [
40, 60,
1750 1230
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "publish", "id": "publish",
"type": "database", "type": "backend",
"label": "Version the bundle", "label": "05 Publish the bundle",
"tag": "Data & AI catalog", "tag": "Data & AI catalog",
"sublabel": "Pin skill + adapter\nand dependency revisions\nRecord approval evidence\nKeep last-known-good", "sublabel": "Pin skill, adapter + dependencies",
"pos": [ "pos": [
40, 60,
2320 1600
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "distribute", "id": "distribute",
"type": "cloud", "type": "backend",
"label": "Verify loaded state", "label": "06 Verify loaded state",
"tag": "Platform & IT", "tag": "Platform & IT",
"sublabel": "Stage and check digest\nRead effective revision\nTest open-session reload\nRevoke unsafe revisions", "sublabel": "Read revision; verify reload",
"pos": [ "pos": [
40, 60,
2890 1970
], ],
"size": [ "size": [
560, 740,
440 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": [ "connections": [
{ {
"id": "flow-0", "id": "owner-review",
"from": "owner", "from": "owner",
"to": "review", "to": "review",
"label": "",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
395
]
}, },
{ {
"id": "flow-1", "id": "review-canary",
"from": "review", "from": "review",
"to": "canary", "to": "canary",
"label": "",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
765
]
}, },
{ {
"id": "flow-2", "id": "canary-approve",
"from": "canary", "from": "canary",
"to": "approve", "to": "approve",
"label": "",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1135
]
}, },
{ {
"id": "flow-3", "id": "approve-publish",
"from": "approve", "from": "approve",
"to": "publish", "to": "publish",
"label": "",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1505
]
}, },
{ {
"id": "flow-4", "id": "publish-distribute",
"from": "publish", "from": "publish",
"to": "distribute", "to": "distribute",
"label": "",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "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
]
} }
] ]
} }
+205 -74
View File
@@ -7,140 +7,271 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
1760,
1420
]
}, },
"components": [ "components": [
{ {
"id": "owner", "id": "owner",
"type": "backend", "type": "external",
"label": "Propose a skill", "label": "01 Propose",
"tag": "Owning team", "tag": "Owning team",
"sublabel": "Problem + intended users\nData and authority scope\nSKILL.md + dependencies\nTestable outcome", "sublabel": "Scope + testable outcome",
"pos": [ "pos": [
40, 80,
40 60
], ],
"size": [ "size": [
560, 500,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "review", "id": "review",
"type": "security", "type": "external",
"label": "Independent review", "label": "02 Review independently",
"tag": "Quality + Security", "tag": "Quality + Security",
"sublabel": "Evaluate useful outcomes\nCheck risk boundaries\nAuthor is not sole approver\nRecord revision evidence", "sublabel": "Author is not sole approver",
"pos": [ "pos": [
700, 300,
40 280
], ],
"size": [ "size": [
560, 500,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "canary", "id": "canary",
"type": "frontend", "type": "external",
"label": "Qualify real use", "label": "03 Qualify real use",
"tag": "Human + AI Agent", "tag": "Human + AI Agent",
"sublabel": "Test exact client build\nRun a bounded canary\nInspect work independently\nNo failed mandatory gate", "sublabel": "Exact client + bounded canary",
"pos": [ "pos": [
1360, 520,
40 500
], ],
"size": [ "size": [
560, 500,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "approve", "id": "approve",
"type": "security", "type": "backend",
"label": "Approve exact scope", "label": "04 Approve exact scope",
"tag": "Accountable approvers", "tag": "Accountable approvers",
"sublabel": "Bind approval to digest\nPermitted uses + expiry\nExceptions cannot bypass\nprotected controls", "sublabel": "Digest + permitted uses + expiry",
"pos": [ "pos": [
1360, 740,
610 720
], ],
"size": [ "size": [
560, 500,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "publish", "id": "publish",
"type": "database", "type": "backend",
"label": "Version the bundle", "label": "05 Publish the bundle",
"tag": "Data & AI catalog", "tag": "Data & AI catalog",
"sublabel": "Pin skill + adapter\nand dependency revisions\nRecord approval evidence\nKeep last-known-good", "sublabel": "Pin skill, adapter + dependencies",
"pos": [ "pos": [
700, 960,
610 940
], ],
"size": [ "size": [
560, 500,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "distribute", "id": "distribute",
"type": "cloud", "type": "backend",
"label": "Verify loaded state", "label": "06 Verify loaded state",
"tag": "Platform & IT", "tag": "Platform & IT",
"sublabel": "Stage and check digest\nRead effective revision\nTest open-session reload\nRevoke unsafe revisions", "sublabel": "Read revision; verify reload",
"pos": [ "pos": [
40, 1180,
610 1160
], ],
"size": [ "size": [
560, 500,
440 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": [ "connections": [
{ {
"id": "flow-0", "id": "owner-review",
"from": "owner", "from": "owner",
"to": "review", "to": "review",
"fromSide": "right", "label": "",
"toSide": "left",
"route": "straight"
},
{
"id": "flow-1",
"from": "review",
"to": "canary",
"fromSide": "right",
"toSide": "left",
"route": "straight"
},
{
"id": "flow-2",
"from": "canary",
"to": "approve",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"via": [
[
330,
255
],
[
550,
255
]
]
}, },
{ {
"id": "flow-3", "id": "review-canary",
"from": "review",
"to": "canary",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
550,
475
],
[
770,
475
]
]
},
{
"id": "canary-approve",
"from": "canary",
"to": "approve",
"label": "",
"fromSide": "bottom",
"toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
770,
695
],
[
990,
695
]
]
},
{
"id": "approve-publish",
"from": "approve", "from": "approve",
"to": "publish", "to": "publish",
"fromSide": "left", "label": "",
"toSide": "right", "fromSide": "bottom",
"route": "straight" "toSide": "top",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
990,
915
],
[
1210,
915
]
]
}, },
{ {
"id": "flow-4", "id": "publish-distribute",
"from": "publish", "from": "publish",
"to": "distribute", "to": "distribute",
"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", "fromSide": "left",
"toSide": "right", "toSide": "right",
"route": "straight" "route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
950,
1220
]
} }
] ]
} }
@@ -7,140 +7,332 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
880,
3080
]
}, },
"components": [ "components": [
{ {
"id": "monitor", "id": "observe",
"type": "database", "type": "external",
"label": "Watch for change", "label": "Observe use",
"tag": "Data & AI + owners", "tag": "Owner + Data & AI",
"sublabel": "Review usefulness + safety\nDue dates and feedback\nModel / client / tool drift\nFailures trigger early review", "sublabel": "Selected evidence + drift",
"pos": [ "pos": [
40, 60,
40 120
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "triage", "id": "triage",
"type": "backend", "type": "external",
"label": "Triage the evidence", "label": "Triage the signal",
"tag": "Owner + Quality", "tag": "Owning team",
"sublabel": "Reproduce the failure\nKeep artifact / client IDs\nAdd sanitized regressions\nAssess business effects", "sublabel": "Useful? Safe? Still needed?",
"pos": [ "pos": [
40, 60,
610 1970
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
},
{
"id": "change",
"type": "external",
"label": "Change the skill",
"tag": "Reviewed correction",
"sublabel": "New immutable revision",
"pos": [
60,
1230
],
"size": [
740,
220
],
"icon": "none"
},
{
"id": "validate",
"type": "external",
"label": "Revalidate",
"tag": "Independent reviewers",
"sublabel": "Regression + client evidence",
"pos": [
60,
860
],
"size": [
740,
220
],
"icon": "none"
},
{
"id": "release",
"type": "backend",
"label": "Approve + version",
"tag": "Accountable human authority",
"sublabel": "Verify distribution and recovery",
"pos": [
60,
490
],
"size": [
740,
220
],
"icon": "none"
},
{
"id": "safe",
"type": "backend",
"label": "Continue safe use",
"tag": "Substantive review passes",
"sublabel": "Keep owner + next review date",
"pos": [
60,
2710
],
"size": [
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", "id": "contain",
"type": "security", "type": "security",
"label": "If unsafe: contain", "label": "Contain unsafe use",
"tag": "Security + Platform", "tag": "Security + Platform & IT",
"sublabel": "Revoke authority\nQuarantine revision\nRestore known-good bundle\nVerify effective state", "sublabel": "Revoke + quarantine + roll back\nNo automatic reinstatement",
"pos": [ "pos": [
40, 60,
1180 1600
], ],
"size": [ "size": [
560, 740,
440 220
]
},
{
"id": "correct",
"type": "backend",
"label": "Create a new revision",
"tag": "Owning team",
"sublabel": "Correct skill + dependencies\nIndependent tests / review\nRequalify client + model\nNo automatic reinstatement",
"pos": [
40,
1750
], ],
"size": [ "icon": "none"
560,
440
]
},
{
"id": "approve",
"type": "security",
"label": "Authorize restart",
"tag": "Accountable human",
"sublabel": "Require regression evidence\nVerify rollback / revocation\nApprove exact new revision\nCanary before wider use",
"pos": [
40,
2320
],
"size": [
560,
440
]
},
{
"id": "retire",
"type": "cloud",
"label": "Retire when obsolete",
"tag": "Data & AI + Platform",
"sublabel": "Deprecate with migration\nRetire: no new invocation\nTrack revoked versions\nHistory stays in metrics",
"pos": [
40,
2890
],
"size": [
560,
440
]
} }
], ],
"connections": [ "connections": [
{ {
"id": "flow-0", "id": "observe-triage",
"from": "monitor", "from": "observe",
"to": "triage", "to": "triage",
"fromSide": "bottom", "label": "",
"toSide": "top", "fromSide": "left",
"route": "straight" "toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
20,
230.0
],
[
20,
2080.0
]
],
"labelAt": [
620,
1930
]
}, },
{ {
"id": "flow-1", "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", "from": "triage",
"to": "contain", "to": "contain",
"fromSide": "bottom", "label": "Unsafe",
"toSide": "top", "fromSide": "top",
"route": "straight" "toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
610,
1875
]
}, },
{ {
"id": "flow-2", "id": "contain-change",
"from": "contain", "from": "contain",
"to": "correct", "to": "change",
"fromSide": "bottom", "label": "Reviewed fix",
"toSide": "top", "fromSide": "top",
"route": "straight" "toSide": "bottom",
"route": "straight",
"width": 4,
"variant": "security",
"labelAt": [
610,
1505
]
}, },
{ {
"id": "flow-3", "id": "safe-observe",
"from": "correct", "from": "safe",
"to": "approve", "to": "observe",
"fromSide": "bottom", "label": "Keep watching",
"toSide": "top", "fromSide": "left",
"route": "straight" "toSide": "left",
}, "route": "straight",
{ "width": 4,
"id": "flow-4", "variant": "default",
"from": "approve", "via": [
"to": "retire", [
"fromSide": "bottom", 42,
"toSide": "top", 2820.0
"route": "straight" ],
[
42,
230.0
]
],
"labelAt": [
620,
80
]
} }
] ]
} }
+320 -96
View File
@@ -7,140 +7,364 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
2630,
1300
]
}, },
"components": [ "components": [
{ {
"id": "monitor", "id": "observe",
"type": "database", "type": "external",
"label": "Watch for change", "label": "Observe use",
"tag": "Data & AI + owners", "tag": "Owner + Data & AI",
"sublabel": "Review usefulness + safety\nDue dates and feedback\nModel / client / tool drift\nFailures trigger early review", "sublabel": "Selected evidence + drift",
"pos": [ "pos": [
40, 670,
40 60
], ],
"size": [ "size": [
560, 560,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "triage", "id": "triage",
"type": "backend", "type": "external",
"label": "Triage the evidence", "label": "Triage the signal",
"tag": "Owner + Quality", "tag": "Owning team",
"sublabel": "Reproduce the failure\nKeep artifact / client IDs\nAdd sanitized regressions\nAssess business effects", "sublabel": "Useful? Safe? Still needed?",
"pos": [ "pos": [
700, 1340,
40 330
],
"size": [
500,
190
],
"icon": "none"
},
{
"id": "change",
"type": "external",
"label": "Change the skill",
"tag": "Reviewed correction",
"sublabel": "New immutable revision",
"pos": [
1240,
750
],
"size": [
520,
180
],
"icon": "none"
},
{
"id": "validate",
"type": "external",
"label": "Revalidate",
"tag": "Independent reviewers",
"sublabel": "Regression + client evidence",
"pos": [
560,
970
], ],
"size": [ "size": [
560, 560,
440 180
] ],
"icon": "none"
},
{
"id": "release",
"type": "backend",
"label": "Approve + version",
"tag": "Accountable human authority",
"sublabel": "Verify distribution and recovery",
"pos": [
70,
570
],
"size": [
540,
180
],
"icon": "none"
},
{
"id": "safe",
"type": "backend",
"label": "Continue safe use",
"tag": "Substantive review passes",
"sublabel": "Keep owner + next review date",
"pos": [
2010,
80
],
"size": [
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", "id": "contain",
"type": "security", "type": "security",
"label": "If unsafe: contain", "label": "Contain unsafe use",
"tag": "Security + Platform", "tag": "Security + Platform & IT",
"sublabel": "Revoke authority\nQuarantine revision\nRestore known-good bundle\nVerify effective state", "sublabel": "Revoke + quarantine + roll back\nNo automatic reinstatement",
"pos": [ "pos": [
1360, 2010,
40 800
], ],
"size": [ "size": [
560, 550,
440 220
]
},
{
"id": "correct",
"type": "backend",
"label": "Create a new revision",
"tag": "Owning team",
"sublabel": "Correct skill + dependencies\nIndependent tests / review\nRequalify client + model\nNo automatic reinstatement",
"pos": [
1360,
610
], ],
"size": [ "icon": "none"
560,
440
]
},
{
"id": "approve",
"type": "security",
"label": "Authorize restart",
"tag": "Accountable human",
"sublabel": "Require regression evidence\nVerify rollback / revocation\nApprove exact new revision\nCanary before wider use",
"pos": [
700,
610
],
"size": [
560,
440
]
},
{
"id": "retire",
"type": "cloud",
"label": "Retire when obsolete",
"tag": "Data & AI + Platform",
"sublabel": "Deprecate with migration\nRetire: no new invocation\nTrack revoked versions\nHistory stays in metrics",
"pos": [
40,
610
],
"size": [
560,
440
]
} }
], ],
"connections": [ "connections": [
{ {
"id": "flow-0", "id": "observe-triage",
"from": "monitor", "from": "observe",
"to": "triage", "to": "triage",
"label": "",
"fromSide": "right", "fromSide": "right",
"toSide": "left", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1590,
145
]
]
}, },
{ {
"id": "flow-1", "id": "triage-change",
"from": "triage", "from": "triage",
"to": "contain", "to": "change",
"fromSide": "right", "label": "Correct",
"toSide": "left",
"route": "straight"
},
{
"id": "flow-2",
"from": "contain",
"to": "correct",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1590,
620
],
[
1500,
620
]
],
"labelAt": [
1450,
680
]
}, },
{ {
"id": "flow-3", "id": "change-validate",
"from": "correct", "from": "change",
"to": "approve", "to": "validate",
"fromSide": "left", "label": "",
"fromSide": "bottom",
"toSide": "right", "toSide": "right",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1500,
1060
]
]
}, },
{ {
"id": "flow-4", "id": "validate-release",
"from": "approve", "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", "to": "retire",
"fromSide": "left", "label": "Retire",
"toSide": "right", "fromSide": "right",
"route": "straight" "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,104 +2,283 @@
"schema_version": 1, "schema_version": 1,
"diagram_type": "architecture", "diagram_type": "architecture",
"meta": { "meta": {
"title": "Recommended collection plan: Measurement", "title": "Candidate metric registry",
"output": "static/diagrams/strategy-measurement-mobile/index.html", "output": "static/diagrams/strategy-measurement-mobile/index.html",
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
880,
2450
]
}, },
"components": [ "components": [
{ {
"id": "baseline", "id": "registry",
"type": "backend", "type": "backend",
"label": "Establish baseline", "label": "Available choices",
"tag": "Comparable workflow", "tag": "Registry, not a rollout plan",
"sublabel": "Join task / PR / CI IDs\nAgree acceptance + window\nRecord revision and client\nKeep unknowns visible", "sublabel": "16 candidate contracts\nUse only what is relevant\nNo mandatory collection",
"pos": [ "pos": [
40, 90,
40 50
], ],
"size": [ "size": [
560, 700,
440 350
] ],
"icon": "none"
}, },
{ {
"id": "success", "id": "qualification",
"type": "frontend", "type": "frontend",
"label": "Accepted outcomes", "label": "Selection + reach",
"tag": "Quality · weekly", "tag": "Qualification / supported use",
"sublabel": "Tracker: starts + acceptance\nAccepted / eligible starts\nwith ended window × 100\nKeep failed, stopped, unknown\nShow outcome coverage", "sublabel": "Precision, recall, coverage\nApproved-workflow adoption",
"pos": [ "pos": [
40, 90,
610 450
], ],
"size": [ "size": [
560, 700,
440 350
]
},
{
"id": "quality",
"type": "security",
"label": "Escaped quality",
"tag": "Quality · monthly",
"sublabel": "PRs + linked defect records\nAffected / followed-up\naccepted changes × 100\nCount each change once\nMissing links ≠ clean",
"pos": [
40,
1180
], ],
"size": [ "icon": "none"
560,
440
]
}, },
{ {
"id": "cost", "id": "outcomes",
"type": "cloud",
"label": "Cost per outcome",
"tag": "Finance · monthly",
"sublabel": "Invoices + review minutes\nAll costs / accepted tasks\nSame matured start cohort\nFailed work + licenses too\nMissing buckets = partial",
"pos": [
40,
1750
],
"size": [
560,
440
]
},
{
"id": "confidence",
"type": "frontend", "type": "frontend",
"label": "Verification confidence", "label": "Outcomes + delivery",
"tag": "People · each cycle", "tag": "Task / change / service",
"sublabel": "Anonymous 1–5 survey\nSum of valid scores / replies\nShow spread; response rate:\nreplies / invitations × 100\nProtect small cohorts", "sublabel": "Success, quality, DORA\nBusiness acceptance",
"pos": [ "pos": [
40, 90,
2320 850
], ],
"size": [ "size": [
560, 700,
440 350
] ],
"icon": "none"
}, },
{ {
"id": "monitor", "id": "experience",
"type": "database", "type": "frontend",
"label": "Ongoing monitoring", "label": "Safety + cost + experience",
"tag": "Catalog + cohort report", "tag": "Policy / spend / people",
"sublabel": "Catalog + cohort records\nTrack support, use, reviews\nCompare with baseline\nShow counts + coverage\nZero denominator = N/A", "sublabel": "Policy effects, outcome cost\nConfidence + cognitive load",
"pos": [ "pos": [
40, 90,
2890 1250
], ],
"size": [ "size": [
560, 700,
440 350
] ],
"icon": "none"
},
{
"id": "health",
"type": "frontend",
"label": "Skill health",
"tag": "Catalog revisions",
"sublabel": "Substantive-review coverage\nOwnership + review evidence",
"pos": [
90,
1650
],
"size": [
700,
350
],
"icon": "none"
},
{
"id": "contract",
"type": "external",
"label": "Inspect a candidate",
"tag": "Every entry explains",
"sublabel": "Meaning + applicability\nCalculation + required inputs\nLimits + implementation status\n9 prototype / 7 unimplemented",
"pos": [
90,
2050
],
"size": [
700,
350
],
"icon": "none"
} }
], ],
"connections": [] "connections": [
} {
"id": "registry-qualification",
"from": "registry",
"to": "qualification",
"label": "",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
45,
225
],
[
45,
625
]
]
},
{
"id": "qualification-contract",
"from": "qualification",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
835,
625
],
[
835,
2225
]
]
},
{
"id": "registry-outcomes",
"from": "registry",
"to": "outcomes",
"label": "",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
45,
225
],
[
45,
1025
]
]
},
{
"id": "outcomes-contract",
"from": "outcomes",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
835,
1025
],
[
835,
2225
]
]
},
{
"id": "registry-experience",
"from": "registry",
"to": "experience",
"label": "",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
45,
225
],
[
45,
1425
]
]
},
{
"id": "experience-contract",
"from": "experience",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
835,
1425
],
[
835,
2225
]
]
},
{
"id": "registry-health",
"from": "registry",
"to": "health",
"label": "",
"fromSide": "left",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
45,
225
],
[
45,
1825
]
]
},
{
"id": "health-contract",
"from": "health",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
835,
1825
],
[
835,
2225
]
]
}
]
}
+249 -70
View File
@@ -2,104 +2,283 @@
"schema_version": 1, "schema_version": 1,
"diagram_type": "architecture", "diagram_type": "architecture",
"meta": { "meta": {
"title": "Recommended collection plan: Measurement", "title": "Candidate metric registry",
"output": "static/diagrams/strategy-measurement/index.html", "output": "static/diagrams/strategy-measurement/index.html",
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
2400,
1120
]
}, },
"components": [ "components": [
{ {
"id": "baseline", "id": "registry",
"type": "backend", "type": "backend",
"label": "Establish baseline", "label": "Available choices",
"tag": "Comparable workflow", "tag": "Registry, not a rollout plan",
"sublabel": "Join task / PR / CI IDs\nAgree acceptance + window\nRecord revision and client\nKeep unknowns visible", "sublabel": "16 candidate contracts\nUse only what is relevant\nNo mandatory collection",
"pos": [ "pos": [
40, 40,
40 405
], ],
"size": [ "size": [
560, 600,
440 310
] ],
"icon": "none"
}, },
{ {
"id": "success", "id": "qualification",
"type": "frontend", "type": "frontend",
"label": "Accepted outcomes", "label": "Selection + reach",
"tag": "Quality · weekly", "tag": "Qualification / supported use",
"sublabel": "Tracker: starts + acceptance\nAccepted / eligible starts\nwith ended window × 100\nKeep failed, stopped, unknown\nShow outcome coverage", "sublabel": "Precision, recall, coverage\nApproved-workflow adoption",
"pos": [ "pos": [
810,
40
],
"size": [
700, 700,
40 220
], ],
"size": [ "icon": "none"
560,
440
]
}, },
{ {
"id": "quality", "id": "outcomes",
"type": "security",
"label": "Escaped quality",
"tag": "Quality · monthly",
"sublabel": "PRs + linked defect records\nAffected / followed-up\naccepted changes × 100\nCount each change once\nMissing links ≠ clean",
"pos": [
1360,
40
],
"size": [
560,
440
]
},
{
"id": "cost",
"type": "cloud",
"label": "Cost per outcome",
"tag": "Finance · monthly",
"sublabel": "Invoices + review minutes\nAll costs / accepted tasks\nSame matured start cohort\nFailed work + licenses too\nMissing buckets = partial",
"pos": [
1360,
610
],
"size": [
560,
440
]
},
{
"id": "confidence",
"type": "frontend", "type": "frontend",
"label": "Verification confidence", "label": "Outcomes + delivery",
"tag": "People · each cycle", "tag": "Task / change / service",
"sublabel": "Anonymous 1–5 survey\nSum of valid scores / replies\nShow spread; response rate:\nreplies / invitations × 100\nProtect small cohorts", "sublabel": "Success, quality, DORA\nBusiness acceptance",
"pos": [ "pos": [
700, 810,
610 310
], ],
"size": [ "size": [
560, 700,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "monitor", "id": "experience",
"type": "database", "type": "frontend",
"label": "Ongoing monitoring", "label": "Safety + cost + experience",
"tag": "Catalog + cohort report", "tag": "Policy / spend / people",
"sublabel": "Catalog + cohort records\nTrack support, use, reviews\nCompare with baseline\nShow counts + coverage\nZero denominator = N/A", "sublabel": "Policy effects, outcome cost\nConfidence + cognitive load",
"pos": [ "pos": [
40, 810,
610 580
], ],
"size": [ "size": [
560, 700,
440 220
] ],
"icon": "none"
},
{
"id": "health",
"type": "frontend",
"label": "Skill health",
"tag": "Catalog revisions",
"sublabel": "Substantive-review coverage\nOwnership + review evidence",
"pos": [
810,
850
],
"size": [
700,
220
],
"icon": "none"
},
{
"id": "contract",
"type": "external",
"label": "Inspect a candidate",
"tag": "Every entry explains",
"sublabel": "Meaning + applicability\nCalculation + required inputs\nLimits + implementation status\n9 prototype / 7 unimplemented",
"pos": [
1710,
355
],
"size": [
650,
360
],
"icon": "none"
} }
], ],
"connections": [] "connections": [
} {
"id": "registry-qualification",
"from": "registry",
"to": "qualification",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
715,
560
],
[
715,
150
]
]
},
{
"id": "qualification-contract",
"from": "qualification",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1605,
150
],
[
1605,
535
]
]
},
{
"id": "registry-outcomes",
"from": "registry",
"to": "outcomes",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
715,
560
],
[
715,
420
]
]
},
{
"id": "outcomes-contract",
"from": "outcomes",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1605,
420
],
[
1605,
535
]
]
},
{
"id": "registry-experience",
"from": "registry",
"to": "experience",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
715,
560
],
[
715,
690
]
]
},
{
"id": "experience-contract",
"from": "experience",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1605,
690
],
[
1605,
535
]
]
},
{
"id": "registry-health",
"from": "registry",
"to": "health",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
715,
560
],
[
715,
960
]
]
},
{
"id": "health-contract",
"from": "health",
"to": "contract",
"label": "",
"fromSide": "right",
"toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1605,
960
],
[
1605,
535
]
]
}
]
}
@@ -7,140 +7,256 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
880,
2800
]
}, },
"components": [ "components": [
{ {
"id": "risk", "id": "selection",
"type": "security", "type": "external",
"label": "Define the envelope", "label": "Right skill?",
"tag": "Owner + reviewers", "tag": "Selection evidence",
"sublabel": "R1: nonsensitive drafting\nR2: code / customer work\nR3: human-authorized\nAgree acceptance rules", "sublabel": "Triggers, near misses, negatives",
"pos": [ "pos": [
40, 60,
40 120
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "compare", "id": "safety",
"type": "backend", "type": "external",
"label": "Compare three paths", "label": "Safe execution?",
"tag": "Same starting artifacts", "tag": "Safety evidence",
"sublabel": "No skill / approved / new\nPositive + negative cases\nRepeat trials per client\nKeep failed attempts", "sublabel": "Permissions + adversarial trials",
"pos": [ "pos": [
40, 60,
610 490
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "inspect", "id": "artifact",
"type": "frontend", "type": "external",
"label": "Inspect the outcome", "label": "Valid artifact?",
"tag": "Independent evidence", "tag": "Independent inspection",
"sublabel": "Right skill selected?\nProcedure safe?\nArtifact checks pass?\nUseful workflow result?", "sublabel": "Tests + human assessment",
"pos": [ "pos": [
40, 60,
1180 860
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
},
{
"id": "outcome",
"type": "external",
"label": "Useful outcome?",
"tag": "Workflow evidence",
"sublabel": "Baseline + repeated trials",
"pos": [
60,
1230
],
"size": [
740,
220
],
"icon": "none"
}, },
{ {
"id": "gate", "id": "gate",
"type": "security", "type": "security",
"label": "Every gate must pass", "label": "Every gate must pass",
"tag": "Quality Engineering", "tag": "Risk sets evaluation depth",
"sublabel": "Safety + authorization\nSchema + provenance\nCritical effect blocks\nMissing coverage blocks", "sublabel": "No averaging away harm\nCritical effect blocks promotion\nMissing coverage = incomplete",
"pos": [ "pos": [
40, 60,
1750 1600
], ],
"size": [ "size": [
560, 740,
440 310
] ],
"icon": "none"
}, },
{ {
"id": "canary", "id": "accept",
"type": "cloud", "type": "backend",
"label": "Test bounded work", "label": "Accept evidence",
"tag": "Human + AI Agent", "tag": "All mandatory gates passed",
"sublabel": "Qualified client + model\nHuman judges usefulness\nShow counts / uncertainty\nEvidence before promotion", "sublabel": "Proceed to scoped approval",
"pos": [ "pos": [
40, 60,
2320 2060
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
}, },
{ {
"id": "improve", "id": "rework",
"type": "database", "type": "security",
"label": "Feed the next revision", "label": "Rework / retest",
"tag": "Owning team", "tag": "Any failure or missing evidence",
"sublabel": "Failures to regression set\nReview corrections\nMaterial client/model drift\ntriggers requalification", "sublabel": "Keep failures in the record",
"pos": [ "pos": [
40, 60,
2890 2430
], ],
"size": [ "size": [
560, 740,
440 220
] ],
"icon": "none"
} }
], ],
"connections": [ "connections": [
{ {
"id": "flow-0", "id": "selection-gate",
"from": "risk", "from": "selection",
"to": "compare",
"fromSide": "bottom",
"toSide": "top",
"route": "straight"
},
{
"id": "flow-1",
"from": "compare",
"to": "inspect",
"fromSide": "bottom",
"toSide": "top",
"route": "straight"
},
{
"id": "flow-2",
"from": "inspect",
"to": "gate", "to": "gate",
"fromSide": "bottom", "label": "",
"toSide": "top", "fromSide": "right",
"route": "straight" "toSide": "right",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
850,
230.0
],
[
850,
1755.0
]
],
"labelAt": [
620,
1560
]
}, },
{ {
"id": "flow-3", "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", "from": "gate",
"to": "canary", "to": "accept",
"label": "Pass",
"fromSide": "bottom", "fromSide": "bottom",
"toSide": "top", "toSide": "top",
"route": "straight" "route": "straight",
"width": 4,
"variant": "default",
"labelAt": [
610,
1965
]
}, },
{ {
"id": "flow-4", "id": "gate-rework",
"from": "canary", "from": "gate",
"to": "improve", "to": "rework",
"fromSide": "bottom", "label": "Fail",
"toSide": "top", "fromSide": "right",
"route": "straight" "toSide": "right",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
824,
1755.0
],
[
824,
2540.0
]
],
"labelAt": [
620,
2390
]
} }
] ]
} }
+211 -91
View File
@@ -7,140 +7,260 @@
"quality_profile": "showcase", "quality_profile": "showcase",
"legend": { "legend": {
"mode": "hidden" "mode": "hidden"
} },
"viewBox": [
2000,
1050
]
}, },
"components": [ "components": [
{ {
"id": "risk", "id": "selection",
"type": "security", "type": "external",
"label": "Define the envelope", "label": "Right skill?",
"tag": "Owner + reviewers", "tag": "Selection evidence",
"sublabel": "R1: nonsensitive drafting\nR2: code / customer work\nR3: human-authorized\nAgree acceptance rules", "sublabel": "Triggers, near misses, negatives",
"pos": [ "pos": [
40, 60,
40 70
], ],
"size": [ "size": [
560, 510,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "compare", "id": "safety",
"type": "backend", "type": "external",
"label": "Compare three paths", "label": "Safe execution?",
"tag": "Same starting artifacts", "tag": "Safety evidence",
"sublabel": "No skill / approved / new\nPositive + negative cases\nRepeat trials per client\nKeep failed attempts", "sublabel": "Permissions + adversarial trials",
"pos": [ "pos": [
700, 60,
40 310
], ],
"size": [ "size": [
560, 510,
440 170
] ],
"icon": "none"
}, },
{ {
"id": "inspect", "id": "artifact",
"type": "frontend", "type": "external",
"label": "Inspect the outcome", "label": "Valid artifact?",
"tag": "Independent evidence", "tag": "Independent inspection",
"sublabel": "Right skill selected?\nProcedure safe?\nArtifact checks pass?\nUseful workflow result?", "sublabel": "Tests + human assessment",
"pos": [ "pos": [
1360, 60,
40 550
], ],
"size": [ "size": [
560, 510,
440 170
] ],
"icon": "none"
},
{
"id": "outcome",
"type": "external",
"label": "Useful outcome?",
"tag": "Workflow evidence",
"sublabel": "Baseline + repeated trials",
"pos": [
60,
790
],
"size": [
510,
170
],
"icon": "none"
}, },
{ {
"id": "gate", "id": "gate",
"type": "security", "type": "security",
"label": "Every gate must pass", "label": "Every gate must pass",
"tag": "Quality Engineering", "tag": "Risk sets evaluation depth",
"sublabel": "Safety + authorization\nSchema + provenance\nCritical effect blocks\nMissing coverage blocks", "sublabel": "No averaging away harm\nCritical effect blocks promotion\nMissing coverage = incomplete",
"pos": [ "pos": [
1360, 740,
610 350
], ],
"size": [ "size": [
560, 520,
440 310
] ],
"icon": "none"
}, },
{ {
"id": "canary", "id": "accept",
"type": "cloud", "type": "backend",
"label": "Test bounded work", "label": "Accept evidence",
"tag": "Human + AI Agent", "tag": "All mandatory gates passed",
"sublabel": "Qualified client + model\nHuman judges usefulness\nShow counts / uncertainty\nEvidence before promotion", "sublabel": "Proceed to scoped approval",
"pos": [ "pos": [
700, 1430,
610 160
], ],
"size": [ "size": [
560, 510,
440 190
] ],
"icon": "none"
}, },
{ {
"id": "improve", "id": "rework",
"type": "database", "type": "security",
"label": "Feed the next revision", "label": "Rework / retest",
"tag": "Owning team", "tag": "Any failure or missing evidence",
"sublabel": "Failures to regression set\nReview corrections\nMaterial client/model drift\ntriggers requalification", "sublabel": "Keep failures in the record",
"pos": [ "pos": [
40, 1430,
610 760
], ],
"size": [ "size": [
560, 510,
440 190
] ],
"icon": "none"
} }
], ],
"connections": [ "connections": [
{ {
"id": "flow-0", "id": "selection-gate",
"from": "risk", "from": "selection",
"to": "compare",
"fromSide": "right",
"toSide": "left",
"route": "straight"
},
{
"id": "flow-1",
"from": "compare",
"to": "inspect",
"fromSide": "right",
"toSide": "left",
"route": "straight"
},
{
"id": "flow-2",
"from": "inspect",
"to": "gate", "to": "gate",
"fromSide": "bottom", "label": "",
"toSide": "top", "fromSide": "right",
"route": "straight" "toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
650,
155
],
[
650,
505
]
]
}, },
{ {
"id": "flow-3", "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", "from": "gate",
"to": "canary", "to": "accept",
"fromSide": "left", "label": "Pass",
"toSide": "right", "fromSide": "right",
"route": "straight" "toSide": "left",
"route": "straight",
"width": 4,
"variant": "default",
"via": [
[
1340,
505
],
[
1340,
255
]
],
"labelAt": [
1330,
210
]
}, },
{ {
"id": "flow-4", "id": "gate-rework",
"from": "canary", "from": "gate",
"to": "improve", "to": "rework",
"fromSide": "left", "label": "Fail",
"toSide": "right", "fromSide": "right",
"route": "straight" "toSide": "left",
"route": "straight",
"width": 4,
"variant": "security",
"via": [
[
1340,
505
],
[
1340,
855
]
],
"labelAt": [
1325,
900
]
} }
] ]
} }
+2 -2
View File
@@ -28,10 +28,10 @@ Include `title` and `description` frontmatter. Place each page in its owning sec
| Contribution | `docs/contribution/` | `sidebars/contribution.ts` | | Contribution | `docs/contribution/` | `sidebars/contribution.ts` |
| Portal maintenance and reference | `docs/core/` | `sidebars.ts` | | 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. Keep these content roots disjoint. Preserve existing URLs unless a URL migration is explicitly approved; provide precise redirects for approved moves. Metric details live on individual canonical pages directly under Measurement, alongside shared collection safeguards and the synthetic example. 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. 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. `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, exact legacy measurement-contracts hash-aware redirect and standalone diagram viewers are non-document HTML routes; no documentation page is exempt. The metric migration manifest in `research/ai-adoption/measurement-migration.json` records old anchors, canonical IDs/URLs, implementation status and preserved-contract hashes.
## Validate the change ## Validate the change
+56
View File
@@ -0,0 +1,56 @@
---
id: baseline
title: Baseline
sidebar_label: Baseline
description: Understand a workflow, select relevant candidate metrics and establish feasible starting evidence for later comparison.
pagination_prev: measurement/index
pagination_next: skills-governance
hide_table_of_contents: true
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
<div className="strategy-content">
Establish a starting position for one workflow as adoption reaches it. This is not a mandatory company-wide baseline of every metric in the [Measurement registry](./measurement/index.mdx).
<ArchifyDiagram name="strategy-baseline" title="From current workflow through relevant selection and feasible evidence to a starting position and later comparison" story />
**Target operating model — no actual baseline values or approved thresholds are established here.** Missing evidence remains an explicit gap, not an invented starting value.
## Understand the current workflow
Describe the task, team or service, intended outcome, current tools, handoffs, controls and known friction. Bound what is in and out of scope. For Delivery, start with codebase structure and existing principles, system-logic sources, tests, builds and packaging, then deployments. Record current practice, including any existing AI use; do not label it “no-skill” unless that is true.
## Select relevant metrics, with reasons
Choose the registry entries that answer this workflow's questions. Record why each selected metric applies, what decision it can inform, and why other candidates are irrelevant or deferred. There is no required universal subset. A service-level DORA metric, a qualification test and a people survey have different populations; they are not interchangeable.
Agree what counts as a started task, independent acceptance and completed follow-up before counting outcomes. Freeze eligibility and acceptance rules and verify event joins. Select a complete comparable workflow cycle and a predeclared precision/sample requirement appropriate to the decision. Do not select a convenient end date after seeing results. Where those requirements cannot be met, record the resulting evidence limitation.
## Agree feasible evidence
Confirm collection feasibility, period, sources, owner, independent reviewer, access, lawful basis, privacy and retention before collecting. Use the [shared safeguards](./measurement/collection-and-safeguards.mdx) and each selected contract's input requirements. Proposed owner/cadence entries are not already assigned responsibilities.
Start with a controlled worksheet or export if automation is unavailable. Join task IDs to acceptance records, PR/commit IDs, CI results and later defects where relevant; test source coverage and permissions. Repository and CI evidence support verification, but a green build alone is not task acceptance. Record exact skill revisions and client/model configurations when present; keep missing versions unknown.
## Record the starting position
Collect prospective current-practice evidence over the agreed period. Record actual starting values only when supported by traceable evidence. Include units, population, observation period, counts, exclusions, source coverage, calculation version, uncertainty and limitations. Zero denominators mean “not available,” not zero performance.
For each selected metric, keep one of these explicit records:
- **Available:** evidenced starting value, source links, calculation, owner and limits.
- **Unavailable:** missing input or permission, affected comparison, responsible owner and proposed next action or review point. Never fill the gap with the synthetic example or an assumed industry value.
Record deferred and non-applicable candidates separately from selected-but-unavailable metrics. This page is the recording method, not an Eldorado baseline report or a set of approved targets.
## Keep later evaluation comparable
Retain task class, service, skill, client/model configuration and concurrent changes. In [Adoption](./skills-adoption.mdx), use the same agreed eligibility, acceptance and observation rules; in [Maintenance](./skills-maintenance.mdx), record any changes and breaks in comparability rather than silently replacing the starting position.
A matched or randomized comparison may be used when practical and ethical. Label observational trends as descriptive, not causal ROI; report confounding, selection and uncertainty. Missing or small samples limit conclusions. Baseline values describe current practice; they do not establish acceptable risk or authorize expansion.
**Next:** [Govern skill ownership and authority →](./skills-governance.mdx)
</div>
-71
View File
@@ -1,71 +0,0 @@
---
id: measurement
title: 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
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
<div className="strategy-content">
Measure adoption in stages: first establish today’s performance, then follow the pilot, extend coverage and keep skills current. Count use separately from outcomes—more use does not by itself mean better work.
<ArchifyDiagram name="strategy-measurement" title="Skills adoption measurement: baseline, pilot, broader adoption and monitoring" story />
**Recommended collection plan · Target operating model, not installed telemetry.** Confirm the proposed roles and reporting cadence below before collecting data.
## 1. Establish the baseline
Choose one workflow, such as reviewing a code change with a review skill. Agree what counts as a started task, independent acceptance and the follow-up period before counting outcomes. Collect current-practice evidence over a complete comparable workflow cycle and until the agreed sample requirement is met; use the same rules in the pilot.
Start with a controlled worksheet or export. Join task IDs to acceptance records, PR/commit IDs, CI results and later defects. Record the exact skill revision and client configuration for pilot work; keep missing versions unknown. Repository and CI evidence support verification, but a green build alone is not task acceptance.
## 2. Measure the pilot
Start with these four questions. Capture inputs as work happens; do not reconstruct review effort from elapsed PR time.
### Are tasks reaching acceptance?
**Collect:** task starts and independent acceptance, failure or abandonment records from the work tracker. **Calculate:** accepted tasks / all eligible starts whose observation period has ended × 100. Failed, abandoned and unresolved tasks stay in the denominator; show unknown outcomes and coverage separately. This is [validated task success](./reference/measurement-contracts.mdx#validated-task-success), not a count of agent completions. **Weekly:** Quality Engineering reviews the pilot cohort.
### Is quality holding up?
**Collect:** accepted change IDs, linked confirmed defects, severity and follow-up dates from review and defect records; retain PR/CI evidence. **Calculate:** changes with at least one confirmed defect / accepted changes with completed follow-up × 100. Count each affected change once, not each defect. Missing links do not mean clean work. **Monthly:** Quality Engineering reviews [escaped quality](./reference/measurement-contracts.mdx#changes-with-escaped-quality-issues) after follow-up, alongside severe incident counts.
### What does an accepted outcome cost?
**Collect:** invoices and usage costs plus sampled human review/rework minutes at approved rates, including unsuccessful attempts and allocated licenses/tooling. **Calculate:** total allocated workflow cost / independently accepted tasks in the same matured start cohort. Keep effort separate from waiting time. Missing buckets make this a partial estimate, not total cost. **Monthly:** Finance & Accounting reconciles [cost per accepted outcome](./reference/measurement-contracts.mdx#cost-per-accepted-outcome), restating late invoices.
### Can people confidently verify the work?
**Collect:** an anonymous 1–5 survey after a pilot workflow cycle: verification confidence, manageable workload and usefulness as separate items. **Calculate:** sum of valid confidence scores / valid responses; also show the distribution and responses / invitations × 100. This guides enablement, not claims of saved time. **After each pilot cycle:** People & Administration reviews [confidence and cognitive load](./reference/measurement-contracts.mdx#verification-confidence-and-cognitive-load); protect small groups.
### What a pilot report looks like
**Illustrative arithmetic, not company results or targets.** The bundled synthetic fixture shows four matured starts: one accepted, one failed, one abandoned and one unresolved. Confirmed success is **1 / 4 × 100 = 25%**, with **75%** known-outcome coverage. A newer start has not finished its observation period and is shown separately.
Recorded compute spend is **EUR 60 / 1 = EUR 60** per accepted outcome—a **partial estimate** because licenses, tooling, review and rework are missing. Five survey responses total 20 points: **20 / 5 = 4** out of 5; response rate is **5 / 10 × 100 = 50%**. The report calls for reconciling the unknown task and missing costs, not claiming a skills benefit. Reproduce it with `node scripts/ai-kpi.mjs`; [fixture and limits](./reference/measurement-contracts.mdx#run-the-synthetic-measurement-example).
## 3. Extend collection as adoption grows
Keep those outcome measures, then add reach. An adoption percentage is meaningless without a known eligible population.
**Are supported skills available?** Freeze the expected active installation inventory; compare observed bundle/adapter revisions with the approved support and revocation records. [Supported bundle coverage](./reference/measurement-contracts.mdx#supported-bundle-coverage) = fresh, verified supported installations / all expected active installations × 100. Offline or stale entries stay in the denominator. **Weekly and at revocation:** Platform & IT reconciles coverage.
**Are approved workflows being used?** Freeze the lawfully enrolled participant cohort; collect workflow-linked use of an approved revision through a qualified adapter or a separately labeled declaration. [Approved-workflow adoption](./reference/measurement-contracts.mdx#approved-workflow-adoption) = eligible participants with at least one observed approved use / eligible invited participants × 100. Report this as an observed lower bound with source coverage: missing use is unknown. Installation and loading are not use. **Monthly:** Data & AI reviews reach without invocation quotas or employee rankings.
## 4. Keep the collection running
**Are skills still maintained?** From the versioned catalog, join each active approved revision to its owner, substantive review evidence and due date. [Review coverage](./reference/measurement-contracts.mdx#skills-within-substantive-review-window) = revisions with all three current / all active approved revisions × 100. **Monthly and each release:** Data & AI reports overdue/ownerless revisions and deprecated or revoked versions still in use—not just a percentage.
At each report, compare like workflows with the baseline and publish the period, counts, exclusions and source coverage. Zero denominators mean “not available.” Trends are descriptive, not causal ROI. Act on critical policy effects immediately; add selection precision/recall at qualification and service-level delivery or departmental outcome measures when their source links are ready.
Agree purpose, lawful basis, access and retention first. No raw prompts, code or secrets by default; suppress people cohorts below five and prevent identifying drill-downs.
[Full KPI reference →](./reference/measurement-contracts.mdx) preserves all definitions, collection safeguards and the distinction between synthetic calculations and unimplemented metrics.
**Next:** [Maintain skills using the evidence →](./skills-maintenance.mdx)
</div>
@@ -0,0 +1,36 @@
---
id: approved-workflow-adoption
slug: /measurement/approved-workflow-adoption
title: Approved-workflow adoption
description: Definition, collection evidence and limitations for approved-workflow adoption.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Eligible participants with at least one approved-skill workflow use; usefulness assessed separately through accepted outcomes and feedback.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct eligible pseudonymous participants with workflow-linked skill.used on approved revision |
| Denominator | Distinct eligible participants in frozen invited cohort |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Consented or otherwise lawfully enrolled pilot cohort for the period; supported workflow available. |
| Exclusions | Outside pilot and formally recorded leave before period; nonresponders remain eligible, not excluded. |
| Events and source | skill.used; cohort.snapshot; optional qualified client adapter or declaration |
| Owner / cadence | Data & AI / Monthly |
| Missing evidence | Show observed lower bound and telemetry/response coverage; missing usage unknown rather than non-use. |
| Gaming and limits | Invocation quotas and coercion; no target usage count, split server/client/self-report origins. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Self-report is not verified use; installation/loading is not use or benefit. Suppress small groups. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: change-fail-rate
slug: /measurement/change-fail-rate
title: Change fail rate
description: Definition, collection evidence and limitations for change fail rate.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Production deployment effects requiring unplanned remediation, rollback or hotfix, not solely outage declarations.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct matured production deployment_ids linked to qualifying immediate intervention |
| Denominator | All distinct production deployment_ids that reached production in the matured deployment cohort |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Deployments with production effect during window and approved intervention follow-up horizon elapsed. |
| Exclusions | Failed preproduction attempts, duplicate callbacks and no-effect retries; failures after production effect stay included. |
| Events and source | deployment.completed; deployment.failed; incident.linked; release and incident records |
| Owner / cadence | Quality Engineering / Weekly; finalized after horizon |
| Missing evidence | Known failures / all eligible deployments is a lower bound where incident linkage incomplete; label provisional and report unknown links. |
| Gaming and limits | Avoid incident links or relabel intervention planned; reconcile rollbacks/hotfixes independently. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Define immediate intervention and follow-up horizon before pilot; no universal incident-to-AI attribution. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: change-lead-time
slug: /measurement/change-lead-time
title: Change lead time
description: Definition, collection evidence and limitations for change lead time.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Arithmetic mean commit-to-first-successful-production-deployment duration; also publish median/p90 and sample count.
| Contract | Definition |
| --- | --- |
| Numerator | Sum of elapsed hours from committed_at to first successful production deployment for each service+commit |
| Denominator | Count of eligible service+commit pairs with valid mappings |
| Expression | N / D |
| Aggregation | mean |
| Unit | hours/change |
| Eligible population | Changes first successfully deployed to a service in reporting window; rollout retries collapse to same logical deployment. |
| Exclusions | Nonproduction, duplicates, no-op deployment with no new commits; missing mappings shown as coverage loss. |
| Events and source | change.committed; deployment.completed; VCS immutable commit timestamps and release manifest |
| Owner / cadence | Platform & IT / Weekly per service |
| Missing evidence | Missing commit mapping or negative/skewed duration excluded from numeric sample, reported explicitly; no zero substitution. |
| Gaming and limits | Timestamp rewriting and cherry-picking fast work; lock change mapping and show undeployed backlog age. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Not PR cycle time, effort saved, or a complete view of unfinished changes. p90 uses nearest-rank sorted sample. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: changes-with-escaped-quality-issues
slug: /measurement/changes-with-escaped-quality-issues
title: Changes with escaped quality issues
description: Definition, collection evidence and limitations for changes with escaped quality issues.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Accepted changes with at least one confirmed post-acceptance defect during approved follow-up, split by severity.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct eligible change_ids with one or more linked confirmed defects in follow-up |
| Denominator | Accepted change_ids with completed predeclared follow-up horizon |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Accepted changes in acceptance window whose follow-up is complete by as_of. |
| Exclusions | Immature changes shown separately; duplicates/unconfirmed reports excluded but counted as pending triage. |
| Events and source | change.accepted; defect.confirmed; defect tracking and acceptance evidence |
| Owner / cadence | Quality Engineering / Monthly mature cohorts |
| Missing evidence | Unknown linkage is a coverage warning; no fabricated clean outcome. Show absolute defect and severe incident counts as well. |
| Gaming and limits | Shorten follow-up or downgrade severity; freeze horizon and rubric and independently sample defects. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Counts affected changes, not raw defects per change; no presumption defects are AI-caused. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,42 @@
---
id: collection-and-safeguards
slug: /measurement/collection-and-safeguards
title: Collection and safeguards
description: Shared interpretation rules, event contracts and privacy safeguards for selected metrics.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** Registry inclusion does not require use, collection or baselining. All owner and cadence entries are proposals to confirm for selected metrics, not assigned responsibilities or approved schedules.
{/* preserved:start */}
Introduction depends on the [roadmap gates](../roadmap.mdx); these pages do not describe installed controls or measured company results.
### Decide what counts before counting
Use the [metric definitions](./index.mdx#metric-definitions) for exact definitions. Every report states its eligible population, observation window, numerator and denominator or distribution sample count, source coverage, exclusions and calculation version. Zero denominators return “not available.” Missing telemetry is unknown, not inactivity, compliance or success. Examples are explicitly synthetic and illustrative.
Begin with task acceptance, repository, CI, deployment and incident systems because they can independently establish outcomes. Add catalog/version inventory and task-level declarations. Client telemetry is supplementary and version-dependent; enabling rich tool details may collect sensitive commands and identifiers. Do not enable it merely to count skills. A normalized adapter may report approved skill identifiers without forwarding raw arguments.
Validated success uses all eligible initiated tasks whose predeclared observation window has matured. Failed and abandoned tasks remain nonsuccess. Unresolved or unobserved outcomes remain in the denominator, with known-outcome coverage shown alongside the conservative confirmed-success share. They are not relabeled as observed failure. A task cancelled before start is excluded; cancellation after start remains visible. Count a task once across retries and multiple skills. For timeliness, also show unfinished work and age; accepted-only lead-time distributions have survivorship limitations.
The registry includes five service-level DORA measures: commit-to-production lead time, deployment frequency, change fail rate, failed deployment recovery time and deployment rework rate. Select them only where applicable; do not substitute PR-open-to-merge time or all-incident MTTR. Where relevant, interpret them alongside selected quality, policy, cost, support, adoption, experience or maintenance evidence. Keep customer/business outcomes linked to their departmental hypotheses; do not infer conversion, trust or revenue improvement from delivery telemetry.
### Interpretation limits {#baseline-and-interpretation}
See [Baseline](../baseline.mdx) for selecting relevant metrics and establishing comparable current-practice evidence. Pilot application belongs to [Adoption](../skills-adoption.mdx); ongoing reporting belongs to [Maintenance](../skills-maintenance.mdx).
Elapsed time and human effort are different quantities. Parallel agents may reduce waiting while increasing review or compute. Include failed attempts and human review/rework in cost, do not multiply a multi-skill task's value across every invocation, and do not convert survey perceptions into proven time savings. Small or incomplete populations yield evidence limits, not an ROI percentage.
### Event and privacy contract
Use a versioned event envelope and required fields per event. Source producer plus event ID deduplicates transport retries. Conflicting payloads under the same key are quarantined. Attempts have distinct IDs under one task. Domain IDs deduplicate deployments, defects and approvals even when multiple feeds describe them. Late arrivals produce versioned aggregate corrections; never silently overwrite a published result. Preserve occurrence and receipt times, reject impossible orderings and flag clock skew. Missing terminal events remain unresolved until the work owner reconciles them.
Many-to-many task/attempt/skill/client associations preserve exact immutable versions. Unknown version stays null with a reason; never assign the latest revision retroactively. Client declarations, server facts and self-reports remain separately labeled. A skill-loaded event is evidence of loading only. The minimal prototype can operate without actor identity except where a lawful, necessary adoption denominator requires pseudonymous cohort membership.
The privacy starting proposal is 30 days for raw structured analytics events and 12 months for aggregated trends, subject to the responsible owners' review. Keep the approved catalog/approval record for the supported revision's lifetime and its required audit period, which must be decided separately. Incident evidence uses a restricted, explicitly authorized hold and deletion schedule, not an indefinite analytics exception. Suppress reports below five people and prohibit drill-down or overlapping cohort slices that reconstruct identities. Five is a proposed minimum, not a guarantee against re-identification. No raw prompts, code, screens, customer cases or secrets are collected by default. Publish the purpose, lawful collection basis, access/deletion route and retention before collecting. Consent is not presumed to be an appropriate employment-law basis.
{/* preserved:end */}
Return to the [Skills Measurement index](./index.mdx) for every metric definition.
</div>
@@ -0,0 +1,36 @@
---
id: confirmed-policy-effects
slug: /measurement/confirmed-policy-effects
title: Confirmed policy effects
description: Definition, collection evidence and limitations for confirmed policy effects.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Eligible evaluated attempts with an actual confirmed prohibited effect; report attempts, blocked attempts and effects separately.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct attempt_ids with confirmed prohibited action effect |
| Denominator | All evaluated eligible attempt_ids including failed and abandoned runs |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Governed skill trials or supported workflow attempts, reported separately for eval and live populations. |
| Exclusions | Out-of-scope runs; detection gaps remain coverage issues, not exclusions from known initiated population. |
| Events and source | attempt.started; policy.violation; gate.blocked; security audit and incident triage |
| Owner / cadence | Architecture & Security / Immediate incident action; weekly aggregates |
| Missing evidence | Unknown outcomes remain visible; zero detected effects never proves safety. Missing audits make rate provisional. |
| Gaming and limits | Hide incidents or inflate harmless trials; absolute severity counts and fixed task mix accompany rate. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Blocked attempts are not actual effects; any critical effect blocks promotion regardless of aggregate rate. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: cost-per-accepted-outcome
slug: /measurement/cost-per-accepted-outcome
title: Cost per accepted outcome
description: Definition, collection evidence and limitations for cost per accepted outcome.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Allocated total workflow cost including unsuccessful attempts, licenses, compute, tooling and human review/rework divided by accepted outcomes.
| Contract | Definition |
| --- | --- |
| Numerator | Sum allocated costs in one approved reporting currency for all tasks/attempts in matured start cohort |
| Denominator | Distinct independently accepted tasks in same cohort |
| Expression | N / D |
| Aggregation | ratio |
| Unit | currency/accepted outcome |
| Eligible population | Matured eligible started-task cohort, with all attempt costs through as_of and allocated shared period costs. |
| Exclusions | Unrelated projects and documented taxes/transfers outside chosen accounting basis; never failed/abandoned attempts. |
| Events and source | cost.recorded; task.accepted; finance invoices, usage ledger, sampled effort with approved rates |
| Owner / cadence | Finance & Accounting Squad / Monthly with late invoice restatement |
| Missing evidence | Missing cost buckets produce partial estimate plus coverage, not a total. Mixed currency requires dated approved conversion; zero accepted outcomes = null with full cost shown. |
| Gaming and limits | Count tokens only or hide labor/failures; reconcile invoice and allocation totals without double counting. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Allocation and sampled labor carry uncertainty; no ROI without independently measured business benefit. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: deployment-frequency
slug: /measurement/deployment-frequency
title: Deployment frequency
description: Definition, collection evidence and limitations for deployment frequency.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Distinct successful logical production deployments per complete service-day of observation.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct service_id+deployment_id successful production deployments in window |
| Denominator | Observed service-days in window |
| Expression | N / D |
| Aggregation | ratio |
| Unit | deployments/service-day |
| Eligible population | Declared production services and complete UTC observation days; report each service before pooled views. |
| Exclusions | Test/staging, failed attempts without production effect, duplicate callbacks and retries of the same rollout. |
| Events and source | deployment.completed; authoritative deployment service |
| Owner / cadence | Platform & IT / Weekly per service |
| Missing evidence | Missing feed periods not zero; report coverage and no unqualified overall rate when period incomplete. |
| Gaming and limits | Split deployments without value; pair with success, rework and failure measures. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. More deployments alone do not establish better performance; affected unsuccessful changes remain in change-fail population. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: deployment-rework-rate
slug: /measurement/deployment-rework-rate
title: Deployment rework rate
description: Definition, collection evidence and limitations for deployment rework rate.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Unplanned production deployment effects triggered by a production incident as a share of deployment effects.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct incident-linked deployment_ids classified unplanned remediation |
| Denominator | All distinct production deployment_ids with production effect in window |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Production deployment effects in window with immutable planned/unplanned classification and incident linkage. |
| Exclusions | Nonproduction, no-effect retries and duplicates; do not exclude unclassified changes silently. |
| Events and source | deployment.completed; incident.linked; release intent classification |
| Owner / cadence | Platform & IT / Weekly |
| Missing evidence | Unknown classification stays denominator; observed numerator is lower bound until reconciliation; show classification coverage. |
| Gaming and limits | Call hotfixes planned; freeze reason and independently reconcile incident/deployment links. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Distinct from failure rate: one failed deployment can lead to multiple separate rework deployments. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: failed-deployment-recovery-time
slug: /measurement/failed-deployment-recovery-time
title: Failed deployment recovery time
description: Definition, collection evidence and limitations for failed deployment recovery time.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Mean elapsed time from production deployment failure onset to service restoration, with median/p90 and unresolved count/age.
| Contract | Definition |
| --- | --- |
| Numerator | Sum of recovery_at minus failure_started_at in hours for recovered eligible deployment failures |
| Denominator | Count of recovered eligible deployment failures with valid timestamps |
| Expression | N / D |
| Aggregation | mean |
| Unit | hours/failure |
| Eligible population | Production deployment failures beginning in window; latest verified restoration if a recovery is rescinded. |
| Exclusions | Nondeployment incidents; invalid timestamp pairs reported missing, not treated as instantaneous recovery. |
| Events and source | deployment.failed; service.restored; incident service |
| Owner / cadence | Platform & IT / Weekly with unresolved failures |
| Missing evidence | Unresolved failures reported separately and excluded only from recovered-duration distribution; prevent declaring improvement without their visibility. |
| Gaming and limits | Premature recovery declarations or omit hard cases; require operational readback and carry unresolved ages. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Not all-incident MTTR; recovered-only sample has survivorship bias. p90 nearest rank. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
+57
View File
@@ -0,0 +1,57 @@
---
id: index
slug: /measurement
title: Skills Measurement
sidebar_label: Measurement
description: Registry of available candidate KPIs and metrics, their applicability, evidence requirements, calculations and limitations.
pagination_prev: stage-01-skills-adoption
pagination_next: baseline
hide_table_of_contents: true
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
<div className="strategy-content">
Browse available candidate KPIs and metrics. Registry inclusion does not require a metric to be used, collected or baselined. Count use separately from outcomes—more use does not by itself mean better work.
<ArchifyDiagram name="strategy-measurement" title="An optional metric registry: browse distinct questions and inspect each contract" story />
**Target operating model — candidate definitions, not installed telemetry or a collection plan.** Proposed owners and cadences in the contracts apply only if selected and agreed for a workflow.
## Read a candidate contract
Each entry explains meaning and applicability, calculation, required inputs and evidence, exclusions, missing-evidence behavior, limitations and implementation status. Selection measures concern qualification; coverage and adoption concern reach; DORA measures concern service delivery. Outcome, safety, cost, experience and maintenance measures retain their distinct populations. A delivery metric need not fit a non-delivery workflow.
To select a relevant subset and establish actual starting evidence, use [Baseline](../baseline.mdx). Pilot application belongs to [Adoption](../skills-adoption.mdx); ongoing interpretation belongs to [Maintenance](../skills-maintenance.mdx).
## Metric definitions
There are **16 contracts: 9 prototype calculations and 7 unimplemented metrics**. The prototype has no network collectors. None of these counts establishes live telemetry.
The nine calculations cover task success, selection precision and recall, change fail rate, deployment rework, escaped quality, policy effects, cost and the primary human-experience item. The seven omissions are supported bundle coverage, approved-workflow adoption, change lead time, deployment frequency, failed deployment recovery time, substantive-review coverage and workflow-specific business acceptance.
Each contract is a prospective definition, not a rollout obligation. No live source or observed baseline is established. N and D refer to the numerator and denominator shown for that metric.
1. [Validated task success](./validated-task-success.mdx) — prototype calculation
2. [Skill selection precision](./skill-selection-precision.mdx) — prototype calculation
3. [Skill selection recall](./skill-selection-recall.mdx) — prototype calculation
4. [Supported bundle coverage](./supported-bundle-coverage.mdx) — unimplemented metric
5. [Approved-workflow adoption](./approved-workflow-adoption.mdx) — unimplemented metric
6. [Change lead time](./change-lead-time.mdx) — unimplemented metric
7. [Deployment frequency](./deployment-frequency.mdx) — unimplemented metric
8. [Change fail rate](./change-fail-rate.mdx) — prototype calculation
9. [Failed deployment recovery time](./failed-deployment-recovery-time.mdx) — unimplemented metric
10. [Deployment rework rate](./deployment-rework-rate.mdx) — prototype calculation
11. [Changes with escaped quality issues](./changes-with-escaped-quality-issues.mdx) — prototype calculation
12. [Confirmed policy effects](./confirmed-policy-effects.mdx) — prototype calculation
13. [Cost per accepted outcome](./cost-per-accepted-outcome.mdx) — prototype calculation
14. [Verification confidence and cognitive load](./verification-confidence-and-cognitive-load.mdx) — prototype calculation
15. [Skills within substantive review window](./skills-within-substantive-review-window.mdx) — unimplemented metric
16. [Workflow-specific business acceptance](./workflow-specific-business-acceptance.mdx) — unimplemented metric
Read [Collection and safeguards](./collection-and-safeguards.mdx) for shared interpretation, event and privacy rules. [Run the synthetic measurement example](./synthetic-example.mdx) for reproducible arithmetic and limits.
**Next:** [Establish a relevant workflow baseline →](../baseline.mdx)
</div>
@@ -0,0 +1,36 @@
---
id: skill-selection-precision
slug: /measurement/skill-selection-precision
title: Skill selection precision
description: Definition, collection evidence and limitations for skill selection precision.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Correct activations among activations on an independently labeled evaluation corpus.
| Contract | Definition |
| --- | --- |
| Numerator | True-positive activations for the requested workflow |
| Denominator | True-positive plus false-positive activations |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | All candidate/client labeled evaluation task-trials with known activation and relevance. |
| Exclusions | Unlabeled/unknown activations excluded with coverage; no cherry-picked failed trials. |
| Events and source | eval.selection; versioned labeled corpus and runner |
| Owner / cadence | Data & AI / Each skill/client qualification |
| Missing evidence | Unknown activation/relevance is unscorable; publish missing count and deny qualification if planned coverage incomplete. |
| Gaming and limits | Choose easy labels or hide negative cases; freeze corpus and independently review labels. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Does not show execution success; precision depends on corpus prevalence. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: skill-selection-recall
slug: /measurement/skill-selection-recall
title: Skill selection recall
description: Definition, collection evidence and limitations for skill selection recall.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Relevant task-trials in which the intended skill activates.
| Contract | Definition |
| --- | --- |
| Numerator | True-positive activations |
| Denominator | True-positive plus false-negative task-trials |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Independently labeled relevant evaluation task-trials for a skill/client tuple. |
| Exclusions | Irrelevant tasks; missing relevance or activation reported as unscorable, not negative. |
| Events and source | eval.selection; versioned labeled corpus and runner |
| Owner / cadence | Data & AI / Each skill/client qualification |
| Missing evidence | Report evaluation coverage; null if no relevant labeled tasks. |
| Gaming and limits | Overtrigger to maximize recall; always pair with precision and negative cases. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Loading the right skill is not adherence. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: skills-within-substantive-review-window
slug: /measurement/skills-within-substantive-review-window
title: Skills within substantive review window
description: Definition, collection evidence and limitations for skills within substantive review window.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Active approved skill revisions with an accountable owner and completed substantive review not past due.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct active approved skill revisions with current owner, review evidence and review_due_at &gt;= as_of |
| Denominator | All active approved skill revisions in catalog snapshot |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Approved revisions currently supported for use at as_of. |
| Exclusions | Experimental and retired revisions; deprecated/revoked in-use counts reported independently, not hidden. |
| Events and source | catalog.published; catalog.reviewed; catalog.deprecated; catalog.revoked; version inventory |
| Owner / cadence | Data & AI / Monthly and each release |
| Missing evidence | Missing owner/date/evidence outside numerator; show ownerless, deprecated-in-use and revoked-in-use absolute counts. |
| Gaming and limits | Rubber-stamp review dates; require linked regression/security/usefulness evidence. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. A current review is not proof of effectiveness; consumer inventory may miss shadow copies. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: supported-bundle-coverage
slug: /measurement/supported-bundle-coverage
title: Supported bundle coverage
description: Definition, collection evidence and limitations for supported bundle coverage.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Inventoried active installations with verified effective approved, supported and nonrevoked bundle/adapter tuple.
| Contract | Definition |
| --- | --- |
| Numerator | Eligible installation_ids with valid inventory evidence within approved freshness window and matching support matrix |
| Denominator | All expected active installation_ids in the managed pilot inventory |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Active installation inventory frozen for report, including invisible/offline entries until authoritative offboarding. |
| Exclusions | Verified decommissioned installations and nonpilot assets; lack of telemetry alone never excludes. |
| Events and source | inventory.observed; catalog.published; catalog.revoked; device inventory |
| Owner / cadence | Platform & IT / Weekly and at revocation |
| Missing evidence | Unknown/stale versions count outside numerator, inside denominator. Show inventory discovery coverage versus eligible device roster; if roster unknown metric is provisional. |
| Gaming and limits | Remove silent devices or trust declared versions; reconcile managed inventory and separately label attestation trust. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Even verified file digests do not prove a private model followed the content. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,22 @@
---
id: synthetic-example
slug: /measurement/synthetic-example
title: Run the synthetic measurement example
description: Reproduce the synthetic KPI calculations and understand their limits.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
{/* preserved:start */}
**Executed synthetic example — not Eldorado results.** The bundled fixture produces 1 independently accepted task from 4 matured starts: 25% confirmed success. One unresolved task remains in that denominator; known-outcome coverage is 75%. Selection precision and recall are each 1 / 2 = 50%. Recorded synthetic compute cost is EUR 60 / 1 accepted outcome = EUR 60 per accepted outcome, a partial estimate: licenses, tooling, review and rework are missing. These deliberately small samples demonstrate arithmetic and missingness, not a baseline, target or ROI claim.
From the private documentation checkout, run `node scripts/ai-kpi.mjs` and `node --test scripts/ai-kpi.test.mjs`. The fixture `scripts/fixtures/ai-kpi-synthetic.json` is labeled synthetic; every result carries the same label and calculation version. This prototype has no network collectors and does not enable employee telemetry.
The executable covers validated success, selection precision/recall, change fail and rework rates, escaped quality, policy effects, cost and human experience. Other catalog metrics remain contracts rather than implemented collectors; output lists the omissions. Snapshot inputs are simplified, pre-reconciled records, not a production ingestion system. Conflicting duplicates reject the calculation rather than silently choosing a value.
{/* preserved:end */}
Return to the [Skills Measurement index](./index.mdx) for every metric definition.
</div>
@@ -0,0 +1,36 @@
---
id: validated-task-success
slug: /measurement/validated-task-success
title: Validated task success
description: Definition, collection evidence and limitations for validated task success.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Confirmed independently accepted tasks as a share of all eligible initiated tasks in a matured start cohort; a conservative confirmed-success share, not observed failure rate.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct matured task_ids with valid independent task.accepted evidence by as_of |
| Denominator | All distinct eligible task_ids started in window with started_at + preregistered task_observation_hours &lt;= as_of |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Tasks in a preregistered workflow cohort, started in window; each counted once across retries, skills and clients. |
| Exclusions | Cancelled before start, sandbox/evaluation tasks and out-of-scope workflows; never post-start failures/abandonments. |
| Events and source | task.started; task.accepted; task.failed; task.abandoned; task.cancelled; work-management acceptance record |
| Owner / cadence | Quality Engineering / Weekly pilot; monthly departmental outcome review |
| Missing evidence | Unresolved/missing terminal outcomes remain in denominator with unknown count and known-outcome coverage; do not label them failed. Invalid/missing start excluded from calculation but flagged as orphan evidence. |
| Gaming and limits | Split easy tasks or omit starts; reconcile starts to approved backlog and audit acceptance rubrics. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Maturation horizon is workflow-specific and must be approved; no automatic abandonment on timeout. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: verification-confidence-and-cognitive-load
slug: /measurement/verification-confidence-and-cognitive-load
title: Verification confidence and cognitive load
description: Definition, collection evidence and limitations for verification confidence and cognitive load.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Prototype calculation — no live telemetry.** The synthetic example calculates this metric; it has no network collectors.
{/* contract:start */}
Three separate anonymous 1–5 item distributions: confidence in verifying output, manageable workload, usefulness. Primary item: verification confidence mean; report full distributions and response rate.
| Contract | Definition |
| --- | --- |
| Numerator | Sum valid 1–5 verification-confidence responses, one per participant per survey wave |
| Denominator | Count valid responses to that item |
| Expression | N / D |
| Aggregation | mean |
| Unit | points (1–5) |
| Eligible population | Invited eligible cohort at fixed survey wave; voluntary responses. |
| Exclusions | Invalid/out-of-range answers and superseded duplicate submission; nonresponders stay in response-rate denominator. |
| Events and source | survey.response; anonymous survey with single-use token and aggregate invitation count |
| Owner / cadence | People & Administration Squad / After pilot workflow cycle; periodic follow-up |
| Missing evidence | Report valid response count / invited count and item nonresponse; no imputation. Suppress people cohorts below five. |
| Gaming and limits | Pressure to respond positively; no manager access to individual responses, avoid identifying free text. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Perception and selection bias; not measured time savings. No composite productivity index. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -0,0 +1,36 @@
---
id: workflow-specific-business-acceptance
slug: /measurement/workflow-specific-business-acceptance
title: Workflow-specific business acceptance
description: Definition, collection evidence and limitations for workflow-specific business acceptance.
---
<div className="strategy-content">
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
**Unimplemented metric — no live telemetry.** This is a prospective contract, not an implemented calculation or collector.
{/* contract:start */}
Independently accepted domain outcomes against a predeclared business rubric, separate from technical completion.
| Contract | Definition |
| --- | --- |
| Numerator | Eligible completed-observation task_ids meeting the department-specific outcome rubric |
| Denominator | All eligible initiated workflow task_ids with matured business-outcome horizon |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | One named departmental workflow and predeclared start cohort; never pool unlike rubrics. |
| Exclusions | Cancelled-before-start and unrelated workflows; unsuccessful or abandoned eligible work remains. |
| Events and source | task.started; business.accepted; departmental case/content/reconciliation/onboarding review record |
| Owner / cadence | Product Management / Each pilot cycle with owning department |
| Missing evidence | Unknown follow-up stays denominator with unresolved count; no claim actual business outcome changed without source evidence. |
| Gaming and limits | Weaken rubric or count drafts as customer benefit; lock rubric and independently audit acceptance. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Domain owner signs acceptance: Operations service accuracy; Marketing claims/rights; Finance reconciliation accuracy; HR policy correctness; Product usability/value. Not revenue uplift. |
{/* contract:end */}
Read the shared [collection and safeguards](./collection-and-safeguards.mdx) before collecting data; see the [synthetic example and limits](./synthetic-example.mdx).
Return to [Skills Measurement](./index.mdx).
</div>
@@ -1,363 +0,0 @@
---
id: measurement-contracts
title: Skills KPI reference
description: Full KPI contracts, minimal events, illustrative calculations, privacy and limits.
---
<div className="strategy-content">
Return to the [Skills Measurement collection plan](../measurement.mdx).
**Target operating model — recommended contracts, not installed collection.** All owner and cadence entries below are proposals to confirm before the pilot, not assigned responsibilities or approved schedules.
There are **16 contracts: 9 prototype calculations and 7 unimplemented metrics**. The prototype has no network collectors. None of these counts establishes live telemetry.
The nine calculations cover task success, selection precision and recall, change fail rate, deployment rework, escaped quality, policy effects, cost and the primary human-experience item. The seven omissions are supported bundle coverage, approved-workflow adoption, change lead time, deployment frequency, failed deployment recovery time, substantive-review coverage and workflow-specific business acceptance.
## Collection and safeguards
Introduction depends on the [roadmap gates](../roadmap.mdx); these pages do not describe installed controls or measured company results.
### Decide what counts before counting
Use the metric contracts below for exact definitions. Every report states its eligible population, observation window, numerator and denominator or distribution sample count, source coverage, exclusions and calculation version. Zero denominators return “not available.” Missing telemetry is unknown, not inactivity, compliance or success. Examples are explicitly synthetic and illustrative.
Begin with task acceptance, repository, CI, deployment and incident systems because they can independently establish outcomes. Add catalog/version inventory and task-level declarations. Client telemetry is supplementary and version-dependent; enabling rich tool details may collect sensitive commands and identifiers. Do not enable it merely to count skills. A normalized adapter may report approved skill identifiers without forwarding raw arguments.
Validated success uses all eligible initiated tasks whose predeclared observation window has matured. Failed and abandoned tasks remain nonsuccess. Unresolved or unobserved outcomes remain in the denominator, with known-outcome coverage shown alongside the conservative confirmed-success share. They are not relabeled as observed failure. A task cancelled before start is excluded; cancellation after start remains visible. Count a task once across retries and multiple skills. For timeliness, also show unfinished work and age; accepted-only lead-time distributions have survivorship limitations.
Track all five service-level DORA measures: commit-to-production lead time, deployment frequency, change fail rate, failed deployment recovery time and deployment rework rate. Do not substitute PR-open-to-merge time or all-incident MTTR. Pair them with matured escaped-quality rates, policy effects, total cost per accepted outcome, support coverage, adoption, human experience and catalog maintenance. Keep customer/business outcomes linked to their departmental hypotheses; do not infer conversion, trust or revenue improvement from delivery telemetry.
### Baseline and interpretation
Before a pilot, freeze eligibility and acceptance rules and verify event joins. Collect prospective no-skill/current-practice evidence over a complete comparable workflow cycle and until the preregistered precision/sample requirement is met. Do not select a convenient end date after seeing results. Retain task class, service, skill, client/model configuration and concurrent changes. A matched or randomized comparison may be used when practical and ethical; label observational trends as descriptive and report confounding, selection and uncertainty.
Elapsed time and human effort are different quantities. Parallel agents may reduce waiting while increasing review or compute. Include failed attempts and human review/rework in cost, do not multiply a multi-skill task's value across every invocation, and do not convert survey perceptions into proven time savings. Small or incomplete populations yield evidence limits, not an ROI percentage.
### Event and privacy contract
Use a versioned event envelope and required fields per event. Source producer plus event ID deduplicates transport retries. Conflicting payloads under the same key are quarantined. Attempts have distinct IDs under one task. Domain IDs deduplicate deployments, defects and approvals even when multiple feeds describe them. Late arrivals produce versioned aggregate corrections; never silently overwrite a published result. Preserve occurrence and receipt times, reject impossible orderings and flag clock skew. Missing terminal events remain unresolved until the work owner reconciles them.
Many-to-many task/attempt/skill/client associations preserve exact immutable versions. Unknown version stays null with a reason; never assign the latest revision retroactively. Client declarations, server facts and self-reports remain separately labeled. A skill-loaded event is evidence of loading only. The minimal prototype can operate without actor identity except where a lawful, necessary adoption denominator requires pseudonymous cohort membership.
The privacy starting proposal is 30 days for raw structured analytics events and 12 months for aggregated trends, subject to the responsible owners' review. Keep the approved catalog/approval record for the supported revision's lifetime and its required audit period, which must be decided separately. Incident evidence uses a restricted, explicitly authorized hold and deletion schedule, not an indefinite analytics exception. Suppress reports below five people and prohibit drill-down or overlapping cohort slices that reconstruct identities. Five is a proposed minimum, not a guarantee against re-identification. No raw prompts, code, screens, customer cases or secrets are collected by default. Publish the purpose, lawful collection basis, access/deletion route and retention before collecting. Consent is not presumed to be an appropriate employment-law basis.
## Metric contracts
Each contract is a prospective collection design. No live source or observed baseline is established. N and D refer to the numerator and denominator shown for that metric.
### Validated task success
Confirmed independently accepted tasks as a share of all eligible initiated tasks in a matured start cohort; a conservative confirmed-success share, not observed failure rate.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct matured task_ids with valid independent task.accepted evidence by as_of |
| Denominator | All distinct eligible task_ids started in window with started_at + preregistered task_observation_hours &lt;= as_of |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Tasks in a preregistered workflow cohort, started in window; each counted once across retries, skills and clients. |
| Exclusions | Cancelled before start, sandbox/evaluation tasks and out-of-scope workflows; never post-start failures/abandonments. |
| Events and source | task.started; task.accepted; task.failed; task.abandoned; task.cancelled; work-management acceptance record |
| Owner / cadence | Quality Engineering / Weekly pilot; monthly departmental outcome review |
| Missing evidence | Unresolved/missing terminal outcomes remain in denominator with unknown count and known-outcome coverage; do not label them failed. Invalid/missing start excluded from calculation but flagged as orphan evidence. |
| Gaming and limits | Split easy tasks or omit starts; reconcile starts to approved backlog and audit acceptance rubrics. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Maturation horizon is workflow-specific and must be approved; no automatic abandonment on timeout. |
### Skill selection precision
Correct activations among activations on an independently labeled evaluation corpus.
| Contract | Definition |
| --- | --- |
| Numerator | True-positive activations for the requested workflow |
| Denominator | True-positive plus false-positive activations |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | All candidate/client labeled evaluation task-trials with known activation and relevance. |
| Exclusions | Unlabeled/unknown activations excluded with coverage; no cherry-picked failed trials. |
| Events and source | eval.selection; versioned labeled corpus and runner |
| Owner / cadence | Data & AI / Each skill/client qualification |
| Missing evidence | Unknown activation/relevance is unscorable; publish missing count and deny qualification if planned coverage incomplete. |
| Gaming and limits | Choose easy labels or hide negative cases; freeze corpus and independently review labels. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Does not show execution success; precision depends on corpus prevalence. |
### Skill selection recall
Relevant task-trials in which the intended skill activates.
| Contract | Definition |
| --- | --- |
| Numerator | True-positive activations |
| Denominator | True-positive plus false-negative task-trials |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Independently labeled relevant evaluation task-trials for a skill/client tuple. |
| Exclusions | Irrelevant tasks; missing relevance or activation reported as unscorable, not negative. |
| Events and source | eval.selection; versioned labeled corpus and runner |
| Owner / cadence | Data & AI / Each skill/client qualification |
| Missing evidence | Report evaluation coverage; null if no relevant labeled tasks. |
| Gaming and limits | Overtrigger to maximize recall; always pair with precision and negative cases. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Loading the right skill is not adherence. |
### Supported bundle coverage
Inventoried active installations with verified effective approved, supported and nonrevoked bundle/adapter tuple.
| Contract | Definition |
| --- | --- |
| Numerator | Eligible installation_ids with valid inventory evidence within approved freshness window and matching support matrix |
| Denominator | All expected active installation_ids in the managed pilot inventory |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Active installation inventory frozen for report, including invisible/offline entries until authoritative offboarding. |
| Exclusions | Verified decommissioned installations and nonpilot assets; lack of telemetry alone never excludes. |
| Events and source | inventory.observed; catalog.published; catalog.revoked; device inventory |
| Owner / cadence | Platform & IT / Weekly and at revocation |
| Missing evidence | Unknown/stale versions count outside numerator, inside denominator. Show inventory discovery coverage versus eligible device roster; if roster unknown metric is provisional. |
| Gaming and limits | Remove silent devices or trust declared versions; reconcile managed inventory and separately label attestation trust. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Even verified file digests do not prove a private model followed the content. |
### Approved-workflow adoption
Eligible participants with at least one approved-skill workflow use; usefulness assessed separately through accepted outcomes and feedback.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct eligible pseudonymous participants with workflow-linked skill.used on approved revision |
| Denominator | Distinct eligible participants in frozen invited cohort |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Consented or otherwise lawfully enrolled pilot cohort for the period; supported workflow available. |
| Exclusions | Outside pilot and formally recorded leave before period; nonresponders remain eligible, not excluded. |
| Events and source | skill.used; cohort.snapshot; optional qualified client adapter or declaration |
| Owner / cadence | Data & AI / Monthly |
| Missing evidence | Show observed lower bound and telemetry/response coverage; missing usage unknown rather than non-use. |
| Gaming and limits | Invocation quotas and coercion; no target usage count, split server/client/self-report origins. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Self-report is not verified use; installation/loading is not use or benefit. Suppress small groups. |
### Change lead time
Arithmetic mean commit-to-first-successful-production-deployment duration; also publish median/p90 and sample count.
| Contract | Definition |
| --- | --- |
| Numerator | Sum of elapsed hours from committed_at to first successful production deployment for each service+commit |
| Denominator | Count of eligible service+commit pairs with valid mappings |
| Expression | N / D |
| Aggregation | mean |
| Unit | hours/change |
| Eligible population | Changes first successfully deployed to a service in reporting window; rollout retries collapse to same logical deployment. |
| Exclusions | Nonproduction, duplicates, no-op deployment with no new commits; missing mappings shown as coverage loss. |
| Events and source | change.committed; deployment.completed; VCS immutable commit timestamps and release manifest |
| Owner / cadence | Platform & IT / Weekly per service |
| Missing evidence | Missing commit mapping or negative/skewed duration excluded from numeric sample, reported explicitly; no zero substitution. |
| Gaming and limits | Timestamp rewriting and cherry-picking fast work; lock change mapping and show undeployed backlog age. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Not PR cycle time, effort saved, or a complete view of unfinished changes. p90 uses nearest-rank sorted sample. |
### Deployment frequency
Distinct successful logical production deployments per complete service-day of observation.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct service_id+deployment_id successful production deployments in window |
| Denominator | Observed service-days in window |
| Expression | N / D |
| Aggregation | ratio |
| Unit | deployments/service-day |
| Eligible population | Declared production services and complete UTC observation days; report each service before pooled views. |
| Exclusions | Test/staging, failed attempts without production effect, duplicate callbacks and retries of the same rollout. |
| Events and source | deployment.completed; authoritative deployment service |
| Owner / cadence | Platform & IT / Weekly per service |
| Missing evidence | Missing feed periods not zero; report coverage and no unqualified overall rate when period incomplete. |
| Gaming and limits | Split deployments without value; pair with success, rework and failure measures. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. More deployments alone do not establish better performance; affected unsuccessful changes remain in change-fail population. |
### Change fail rate
Production deployment effects requiring unplanned remediation, rollback or hotfix, not solely outage declarations.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct matured production deployment_ids linked to qualifying immediate intervention |
| Denominator | All distinct production deployment_ids that reached production in the matured deployment cohort |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Deployments with production effect during window and approved intervention follow-up horizon elapsed. |
| Exclusions | Failed preproduction attempts, duplicate callbacks and no-effect retries; failures after production effect stay included. |
| Events and source | deployment.completed; deployment.failed; incident.linked; release and incident records |
| Owner / cadence | Quality Engineering / Weekly; finalized after horizon |
| Missing evidence | Known failures / all eligible deployments is a lower bound where incident linkage incomplete; label provisional and report unknown links. |
| Gaming and limits | Avoid incident links or relabel intervention planned; reconcile rollbacks/hotfixes independently. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Define immediate intervention and follow-up horizon before pilot; no universal incident-to-AI attribution. |
### Failed deployment recovery time
Mean elapsed time from production deployment failure onset to service restoration, with median/p90 and unresolved count/age.
| Contract | Definition |
| --- | --- |
| Numerator | Sum of recovery_at minus failure_started_at in hours for recovered eligible deployment failures |
| Denominator | Count of recovered eligible deployment failures with valid timestamps |
| Expression | N / D |
| Aggregation | mean |
| Unit | hours/failure |
| Eligible population | Production deployment failures beginning in window; latest verified restoration if a recovery is rescinded. |
| Exclusions | Nondeployment incidents; invalid timestamp pairs reported missing, not treated as instantaneous recovery. |
| Events and source | deployment.failed; service.restored; incident service |
| Owner / cadence | Platform & IT / Weekly with unresolved failures |
| Missing evidence | Unresolved failures reported separately and excluded only from recovered-duration distribution; prevent declaring improvement without their visibility. |
| Gaming and limits | Premature recovery declarations or omit hard cases; require operational readback and carry unresolved ages. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Not all-incident MTTR; recovered-only sample has survivorship bias. p90 nearest rank. |
### Deployment rework rate
Unplanned production deployment effects triggered by a production incident as a share of deployment effects.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct incident-linked deployment_ids classified unplanned remediation |
| Denominator | All distinct production deployment_ids with production effect in window |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Production deployment effects in window with immutable planned/unplanned classification and incident linkage. |
| Exclusions | Nonproduction, no-effect retries and duplicates; do not exclude unclassified changes silently. |
| Events and source | deployment.completed; incident.linked; release intent classification |
| Owner / cadence | Platform & IT / Weekly |
| Missing evidence | Unknown classification stays denominator; observed numerator is lower bound until reconciliation; show classification coverage. |
| Gaming and limits | Call hotfixes planned; freeze reason and independently reconcile incident/deployment links. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Distinct from failure rate: one failed deployment can lead to multiple separate rework deployments. |
### Changes with escaped quality issues
Accepted changes with at least one confirmed post-acceptance defect during approved follow-up, split by severity.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct eligible change_ids with one or more linked confirmed defects in follow-up |
| Denominator | Accepted change_ids with completed predeclared follow-up horizon |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Accepted changes in acceptance window whose follow-up is complete by as_of. |
| Exclusions | Immature changes shown separately; duplicates/unconfirmed reports excluded but counted as pending triage. |
| Events and source | change.accepted; defect.confirmed; defect tracking and acceptance evidence |
| Owner / cadence | Quality Engineering / Monthly mature cohorts |
| Missing evidence | Unknown linkage is a coverage warning; no fabricated clean outcome. Show absolute defect and severe incident counts as well. |
| Gaming and limits | Shorten follow-up or downgrade severity; freeze horizon and rubric and independently sample defects. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Counts affected changes, not raw defects per change; no presumption defects are AI-caused. |
### Confirmed policy effects
Eligible evaluated attempts with an actual confirmed prohibited effect; report attempts, blocked attempts and effects separately.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct attempt_ids with confirmed prohibited action effect |
| Denominator | All evaluated eligible attempt_ids including failed and abandoned runs |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Governed skill trials or supported workflow attempts, reported separately for eval and live populations. |
| Exclusions | Out-of-scope runs; detection gaps remain coverage issues, not exclusions from known initiated population. |
| Events and source | attempt.started; policy.violation; gate.blocked; security audit and incident triage |
| Owner / cadence | Architecture & Security / Immediate incident action; weekly aggregates |
| Missing evidence | Unknown outcomes remain visible; zero detected effects never proves safety. Missing audits make rate provisional. |
| Gaming and limits | Hide incidents or inflate harmless trials; absolute severity counts and fixed task mix accompany rate. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Blocked attempts are not actual effects; any critical effect blocks promotion regardless of aggregate rate. |
### Cost per accepted outcome
Allocated total workflow cost including unsuccessful attempts, licenses, compute, tooling and human review/rework divided by accepted outcomes.
| Contract | Definition |
| --- | --- |
| Numerator | Sum allocated costs in one approved reporting currency for all tasks/attempts in matured start cohort |
| Denominator | Distinct independently accepted tasks in same cohort |
| Expression | N / D |
| Aggregation | ratio |
| Unit | currency/accepted outcome |
| Eligible population | Matured eligible started-task cohort, with all attempt costs through as_of and allocated shared period costs. |
| Exclusions | Unrelated projects and documented taxes/transfers outside chosen accounting basis; never failed/abandoned attempts. |
| Events and source | cost.recorded; task.accepted; finance invoices, usage ledger, sampled effort with approved rates |
| Owner / cadence | Finance & Accounting Squad / Monthly with late invoice restatement |
| Missing evidence | Missing cost buckets produce partial estimate plus coverage, not a total. Mixed currency requires dated approved conversion; zero accepted outcomes = null with full cost shown. |
| Gaming and limits | Count tokens only or hide labor/failures; reconcile invoice and allocation totals without double counting. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Allocation and sampled labor carry uncertainty; no ROI without independently measured business benefit. |
### Verification confidence and cognitive load
Three separate anonymous 1–5 item distributions: confidence in verifying output, manageable workload, usefulness. Primary item: verification confidence mean; report full distributions and response rate.
| Contract | Definition |
| --- | --- |
| Numerator | Sum valid 1–5 verification-confidence responses, one per participant per survey wave |
| Denominator | Count valid responses to that item |
| Expression | N / D |
| Aggregation | mean |
| Unit | points (1–5) |
| Eligible population | Invited eligible cohort at fixed survey wave; voluntary responses. |
| Exclusions | Invalid/out-of-range answers and superseded duplicate submission; nonresponders stay in response-rate denominator. |
| Events and source | survey.response; anonymous survey with single-use token and aggregate invitation count |
| Owner / cadence | People & Administration Squad / After pilot workflow cycle; periodic follow-up |
| Missing evidence | Report valid response count / invited count and item nonresponse; no imputation. Suppress people cohorts below five. |
| Gaming and limits | Pressure to respond positively; no manager access to individual responses, avoid identifying free text. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Perception and selection bias; not measured time savings. No composite productivity index. |
### Skills within substantive review window
Active approved skill revisions with an accountable owner and completed substantive review not past due.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct active approved skill revisions with current owner, review evidence and review_due_at &gt;= as_of |
| Denominator | All active approved skill revisions in catalog snapshot |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Approved revisions currently supported for use at as_of. |
| Exclusions | Experimental and retired revisions; deprecated/revoked in-use counts reported independently, not hidden. |
| Events and source | catalog.published; catalog.reviewed; catalog.deprecated; catalog.revoked; version inventory |
| Owner / cadence | Data & AI / Monthly and each release |
| Missing evidence | Missing owner/date/evidence outside numerator; show ownerless, deprecated-in-use and revoked-in-use absolute counts. |
| Gaming and limits | Rubber-stamp review dates; require linked regression/security/usefulness evidence. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. A current review is not proof of effectiveness; consumer inventory may miss shadow copies. |
### Workflow-specific business acceptance
Independently accepted domain outcomes against a predeclared business rubric, separate from technical completion.
| Contract | Definition |
| --- | --- |
| Numerator | Eligible completed-observation task_ids meeting the department-specific outcome rubric |
| Denominator | All eligible initiated workflow task_ids with matured business-outcome horizon |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | One named departmental workflow and predeclared start cohort; never pool unlike rubrics. |
| Exclusions | Cancelled-before-start and unrelated workflows; unsuccessful or abandoned eligible work remains. |
| Events and source | task.started; business.accepted; departmental case/content/reconciliation/onboarding review record |
| Owner / cadence | Product Management / Each pilot cycle with owning department |
| Missing evidence | Unknown follow-up stays denominator with unresolved count; no claim actual business outcome changed without source evidence. |
| Gaming and limits | Weaken rubric or count drafts as customer benefit; lock rubric and independently audit acceptance. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Domain owner signs acceptance: Operations service accuracy; Marketing claims/rights; Finance reconciliation accuracy; HR policy correctness; Product usability/value. Not revenue uplift. |
## Run the synthetic measurement example
**Executed synthetic example — not Eldorado results.** The bundled fixture produces 1 independently accepted task from 4 matured starts: 25% confirmed success. One unresolved task remains in that denominator; known-outcome coverage is 75%. Selection precision and recall are each 1 / 2 = 50%. Recorded synthetic compute cost is EUR 60 / 1 accepted outcome = EUR 60 per accepted outcome, a partial estimate: licenses, tooling, review and rework are missing. These deliberately small samples demonstrate arithmetic and missingness, not a baseline, target or ROI claim.
From the private documentation checkout, run `node scripts/ai-kpi.mjs` and `node --test scripts/ai-kpi.test.mjs`. The fixture `scripts/fixtures/ai-kpi-synthetic.json` is labeled synthetic; every result carries the same label and calculation version. This prototype has no network collectors and does not enable employee telemetry.
The executable covers validated success, selection precision/recall, change fail and rework rates, escaped quality, policy effects, cost and human experience. Other catalog metrics remain contracts rather than implemented collectors; output lists the omissions. Snapshot inputs are simplified, pre-reconciled records, not a production ingestion system. Conflicting duplicates reject the calculation rather than silently choosing a value.
**Next:** [Use findings to maintain, quarantine or replace skills →](../skills-maintenance.mdx)
</div>
+1 -1
View File
@@ -33,7 +33,7 @@ Repeat this loop within every phase—not as six global roadmap stages. Skills t
**Understand the engineering path first:** codebase structure and existing principles → system-logic source → tests → builds and packaging → deployments. Inspect actual practices, tooling, controls and problems with the people doing the work, rather than assuming the documented process is the real one. **Understand the engineering path first:** codebase structure and existing principles → system-logic source → tests → builds and packaging → deployments. Inspect actual practices, tooling, controls and problems with the people doing the work, rather than assuming the documented process is the real one.
**Establish the comparison, then engineer skills.** Define metrics/KPIs and a baseline for comparable work before changing it. [Skills Measurement](./measurement.mdx) explains collection and calculation. Connect reviewed skills to the engineering path, with clear triggers, dependencies and acceptance checks. **Establish the comparison, then engineer skills.** Define metrics/KPIs and a baseline for comparable work before changing it. [Skills Measurement](./measurement/index.mdx) explains collection and calculation. Connect reviewed skills to the engineering path, with clear triggers, dependencies and acceptance checks.
**Use, collect, compare, repeat.** A human works with an AI Agent on real changes. Collect results, friction and feedback alongside quality, cost and verification effort. Compare with the baseline to understand the new current situation; revise the skills and working practices, then run the loop again. **Use, collect, compare, repeat.** A human works with an AI Agent on real changes. Collect results, friction and feedback alongside quality, cost and verification effort. Compare with the baseline to understand the new current situation; revise the skills and working practices, then run the loop again.
+31 -1
View File
@@ -36,6 +36,36 @@ No developer rankings, individual productivity scores, minimum invocation quotas
</details> </details>
**Next:** [Measure accepted outcomes, not activity →](./measurement.mdx) <details>
<summary>Pilot evidence, illustrative report and expansion</summary>
## Apply the selected measures in the pilot
Use the relevant subset agreed in [Baseline](./baseline.mdx), not every entry in the [Measurement registry](./measurement/index.mdx). Keep its eligibility, independent acceptance and follow-up rules. Capture inputs as work happens; record exact skill revision and client configuration, leaving missing versions unknown. Do not reconstruct review effort from elapsed PR time.
The following questions are candidates, not a mandatory pilot bundle. Proposed owners and cadences need agreement before collection:
| If this question is selected | Evidence and pilot review |
| --- | --- |
| Are tasks reaching acceptance? | Join tracker starts to independent acceptance, failure and abandonment. Apply [validated task success](./measurement/validated-task-success.mdx) to matured starts; retain failed, abandoned and unresolved tasks in the denominator and show unknown outcomes and coverage. Proposed weekly reviewer: Quality Engineering. |
| Is quality holding up? | Link accepted changes to confirmed defects, severity and follow-up dates; retain PR/CI evidence. Apply [escaped quality](./measurement/changes-with-escaped-quality-issues.mdx) after follow-up, counting each affected change once, alongside severe incidents. Missing links are not clean work. Proposed monthly reviewer: Quality Engineering. |
| What does an accepted outcome cost? | Reconcile invoices and usage, sampled review/rework minutes at approved rates, unsuccessful attempts and allocated licenses/tooling. Apply [cost per accepted outcome](./measurement/cost-per-accepted-outcome.mdx) to the same matured start cohort; distinguish effort from waiting. Missing buckets make the estimate partial. Proposed monthly reviewer: Finance & Accounting; restate late invoices. |
| Can people confidently verify the work? | Use the anonymous 1–5 survey for confidence, manageable workload and usefulness as separate items. Report the [confidence measure](./measurement/verification-confidence-and-cognitive-load.mdx), distribution and response rate; protect small groups. Proposed reviewer after each pilot cycle: People & Administration. This is enablement evidence, not proven saved time. |
### What a pilot report looks like
**Illustrative arithmetic, not company results or targets.** The bundled synthetic fixture shows four matured starts: one accepted, one failed, one abandoned and one unresolved. Confirmed success is **1 / 4 × 100 = 25%**, with **75%** known-outcome coverage. A newer start has not finished its observation period and is shown separately.
Recorded compute spend is **EUR 60 / 1 = EUR 60** per accepted outcome—a **partial estimate** because licenses, tooling, review and rework are missing. Five survey responses total 20 points: **20 / 5 = 4** out of 5; response rate is **5 / 10 × 100 = 50%**. Reconcile the unknown task and missing costs instead of claiming a skills benefit. Reproduce it with `node scripts/ai-kpi.mjs`; [fixture and limits](./measurement/synthetic-example.mdx).
### Revisit selection as adoption expands
Keep relevant outcome evidence comparable; add reach measures only if they answer the expanded workflow's questions. For [supported bundle coverage](./measurement/supported-bundle-coverage.mdx), freeze the expected active installation inventory and compare observed bundle/adapter revisions to support and revocation records. Offline or stale entries stay in the denominator. Platform & IT is the proposed weekly and at-revocation reviewer.
For [approved-workflow adoption](./measurement/approved-workflow-adoption.mdx), freeze the lawfully enrolled participant cohort and distinguish qualified-adapter observations from labeled declarations. Report the observed lower bound with source coverage: missing use is unknown; installation and loading are not use. Data & AI is the proposed monthly reviewer. No invocation quotas or employee rankings. Return to Baseline for a new workflow rather than treating rollout as company-wide metric collection.
</details>
**Next:** [Maintain skills using the agreed evidence →](./skills-maintenance.mdx)
</div> </div>
+15
View File
@@ -44,6 +44,21 @@ Owner ratification is required before adoption. Review earlier after incidents o
</details> </details>
<details>
<summary>Ongoing reporting and comparison</summary>
## Keep selected evidence useful
Continue the measures agreed for the workflow in [Baseline](./baseline.mdx) and applied during [Adoption](./skills-adoption.mdx). Revisit relevance when scope changes; the [registry](./measurement/index.mdx) is a source of choices, not a requirement to collect every metric.
If substantive-review coverage is selected, join each active approved catalog revision to its owner, substantive review evidence and due date. Apply [review coverage](./measurement/skills-within-substantive-review-window.mdx): revisions with all three current / all active approved revisions × 100. The proposed Data & AI review is monthly and at each release, subject to agreement. Report overdue/ownerless revisions and deprecated or revoked versions still in use—not just a percentage.
At each agreed report, compare like workflows with their recorded starting position. Publish period, counts, exclusions, source coverage and changes in calculation or scope. Zero denominators mean “not available”; unresolved gaps remain visible. Trends are descriptive, not causal ROI. Reconcile late evidence using versioned corrections and distinguish changed practice from changed measurement.
Act on critical policy effects immediately; do not wait for a reporting cycle. Use selection precision/recall at qualification, or service-level delivery and departmental outcome measures, only where relevant and their source links are feasible. Confirm purpose, lawful basis, access and retention before changing collection, following the [shared safeguards](./measurement/collection-and-safeguards.mdx).
</details>
**Next:** [Stage - 02 - Autonomous Agent →](./stage-02-autonomous-agent.mdx) **Next:** [Stage - 02 - Autonomous Agent →](./stage-02-autonomous-agent.mdx)
</div> </div>
+7 -2
View File
@@ -19,18 +19,23 @@ Adoption then extends to other SDLC phases; their order is not yet decided. Deli
## Repeat within each phase ## 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.** **Understand the current situation → select relevant KPIs and establish a workflow baseline → engineer skills topology → use skills in real work → collect agreed 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. 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 ## Put the foundation into practice
- [Measurement](./measurement/index.mdx) — browse available candidate metrics; inclusion does not require collection or baselining.
- [Baseline](./baseline.mdx) — understand a workflow, select relevant metrics and record feasible starting evidence or explicit gaps.
- [Governance](./skills-governance.mdx) — define ownership, authority and approval of skills. - [Governance](./skills-governance.mdx) — define ownership, authority and approval of skills.
- [Quality](./skills-quality.mdx) — qualify skills and verify their outcomes before wider use. - [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. - [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. - [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. Proven skills, measurements and working practices support [Stage - 02 - Autonomous Agent](./stage-02-autonomous-agent.mdx); adoption alone does not grant autonomous execution authority.
These six topics belong to Stage 01, not six additional roadmap stages. Establish a baseline per workflow as adoption reaches it—not a mandatory company-wide baseline of every registry metric.
**Next:** [Browse the Measurement registry →](./measurement/index.mdx)
</div> </div>
+1 -1
View File
@@ -13,7 +13,7 @@ This future stage of the [Roadmap](./roadmap.mdx) builds on [Stage - 01 - Skills
## Build on what has been proven ## 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. 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/index.mdx) and [maintenance](./skills-maintenance.mdx); it is not permission to expand authority automatically.
## Introduce authority deliberately ## Introduce authority deliberately
+1 -1
View File
@@ -157,7 +157,7 @@ Success means accepted outcomes with maintained quality, controlled risk and und
- [Skills governance](./skills-governance.mdx) — Contract, lifecycle, decision rights, adapters, locking, exceptions. - [Skills governance](./skills-governance.mdx) — Contract, lifecycle, decision rights, adapters, locking, exceptions.
- [Skills quality](./skills-quality.mdx) — Risk tiers, independent evaluation, repeated trials, adversarial conformance. - [Skills quality](./skills-quality.mdx) — Risk tiers, independent evaluation, repeated trials, adversarial conformance.
- [Skills adoption](./skills-adoption.mdx) — Workflow-led coaching, client choice, feedback, manual path. - [Skills adoption](./skills-adoption.mdx) — Workflow-led coaching, client choice, feedback, manual path.
- [Measurement](./measurement.mdx) — Full KPI contracts, minimal events, illustrative calculations, privacy and limits. - [Measurement](./measurement/index.mdx) — Full KPI contracts, minimal events, illustrative calculations, privacy and limits.
- [Skills maintenance](./skills-maintenance.mdx) — Review, drift, deprecation, rollback and incident handling. - [Skills maintenance](./skills-maintenance.mdx) — Review, drift, deprecation, rollback and incident handling.
</details> </details>
+1 -1
View File
@@ -15,7 +15,7 @@
"verify:build": "node scripts/verify.mjs --build", "verify:build": "node scripts/verify.mjs --build",
"diagrams:build": "node scripts/build-diagrams.mjs", "diagrams:build": "node scripts/build-diagrams.mjs",
"test": "node --test scripts/*.test.mjs", "test": "node --test scripts/*.test.mjs",
"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", "test:browser": "node scripts/browser/measurement-pages.cjs && 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" "kpi:example": "node scripts/ai-kpi.mjs"
}, },
"dependencies": { "dependencies": {
@@ -0,0 +1,162 @@
{
"sourceCommit": "b9c2047c614dd4e825fa746815104d35d9500739",
"sourcePath": "docs/strategy/reference/measurement-contracts.mdx",
"metrics": [
{
"oldAnchor": "validated-task-success",
"title": "Validated task success",
"docId": "measurement/validated-task-success",
"url": "/strategy/measurement/validated-task-success/",
"calculationId": "validated-task-success",
"status": "prototype",
"contractSha256": "925100f4c84f43e2cfdca4a89d6b4a984c808dae2d2ec5e57202318f70b9c544"
},
{
"oldAnchor": "skill-selection-precision",
"title": "Skill selection precision",
"docId": "measurement/skill-selection-precision",
"url": "/strategy/measurement/skill-selection-precision/",
"calculationId": "selection-precision",
"status": "prototype",
"contractSha256": "189e93e4e5f52a7e70e2045c6b824a9da621382f39ba8ce9a59c0c04812f0ce3"
},
{
"oldAnchor": "skill-selection-recall",
"title": "Skill selection recall",
"docId": "measurement/skill-selection-recall",
"url": "/strategy/measurement/skill-selection-recall/",
"calculationId": "selection-recall",
"status": "prototype",
"contractSha256": "847b5ba6094585eb88b8eb2b55b42e0f3c6822dcaa1629256ad0501a13eb7f18"
},
{
"oldAnchor": "supported-bundle-coverage",
"title": "Supported bundle coverage",
"docId": "measurement/supported-bundle-coverage",
"url": "/strategy/measurement/supported-bundle-coverage/",
"calculationId": "supported-bundle-coverage",
"status": "unimplemented",
"contractSha256": "8b4dc2dbdfb6999fac283814f518645d0830d515125910d79dd7dc8763f0521c"
},
{
"oldAnchor": "approved-workflow-adoption",
"title": "Approved-workflow adoption",
"docId": "measurement/approved-workflow-adoption",
"url": "/strategy/measurement/approved-workflow-adoption/",
"calculationId": "useful-adoption",
"status": "unimplemented",
"contractSha256": "355996d5adb603021ba22f9e1f7f08f2fbea017dac9ee0a4bd42cc00bf7adea3"
},
{
"oldAnchor": "change-lead-time",
"title": "Change lead time",
"docId": "measurement/change-lead-time",
"url": "/strategy/measurement/change-lead-time/",
"calculationId": "change-lead-time",
"status": "unimplemented",
"contractSha256": "96fd0edd5eba139a7c5905b606bcc96e608c5294acd2ef0c962fdb7d339d8251"
},
{
"oldAnchor": "deployment-frequency",
"title": "Deployment frequency",
"docId": "measurement/deployment-frequency",
"url": "/strategy/measurement/deployment-frequency/",
"calculationId": "deployment-frequency",
"status": "unimplemented",
"contractSha256": "5a34ba52e5c2768e29a1a7574274e07a65509f75063e89ccc7ce1b035aeff4c5"
},
{
"oldAnchor": "change-fail-rate",
"title": "Change fail rate",
"docId": "measurement/change-fail-rate",
"url": "/strategy/measurement/change-fail-rate/",
"calculationId": "change-fail-rate",
"status": "prototype",
"contractSha256": "d2a4ecaddd468f42db5824f9bfab8a8c8fcdf9157b33b4e4f047269a83c8567b"
},
{
"oldAnchor": "failed-deployment-recovery-time",
"title": "Failed deployment recovery time",
"docId": "measurement/failed-deployment-recovery-time",
"url": "/strategy/measurement/failed-deployment-recovery-time/",
"calculationId": "failed-deployment-recovery-time",
"status": "unimplemented",
"contractSha256": "489fd756883d4ccee1769fcfaa0b67725420463df2b1f6faf3e7faa7002f7a80"
},
{
"oldAnchor": "deployment-rework-rate",
"title": "Deployment rework rate",
"docId": "measurement/deployment-rework-rate",
"url": "/strategy/measurement/deployment-rework-rate/",
"calculationId": "deployment-rework-rate",
"status": "prototype",
"contractSha256": "e8ade457136feac3ad39e9eba2a9d67142bc605727d9f4f038a7df6d5a0707fd"
},
{
"oldAnchor": "changes-with-escaped-quality-issues",
"title": "Changes with escaped quality issues",
"docId": "measurement/changes-with-escaped-quality-issues",
"url": "/strategy/measurement/changes-with-escaped-quality-issues/",
"calculationId": "escaped-quality-rate",
"status": "prototype",
"contractSha256": "c436d504c8b3a9f655de18b2ca00fa8a0ab47ea1cf37637ff21561f81ecc77c9"
},
{
"oldAnchor": "confirmed-policy-effects",
"title": "Confirmed policy effects",
"docId": "measurement/confirmed-policy-effects",
"url": "/strategy/measurement/confirmed-policy-effects/",
"calculationId": "policy-effects",
"status": "prototype",
"contractSha256": "b64c02f5516b08421fa8b816f0f490571afd05baf5ad6e9d92cb6a7b711ac82a"
},
{
"oldAnchor": "cost-per-accepted-outcome",
"title": "Cost per accepted outcome",
"docId": "measurement/cost-per-accepted-outcome",
"url": "/strategy/measurement/cost-per-accepted-outcome/",
"calculationId": "cost-per-accepted-outcome",
"status": "prototype",
"contractSha256": "d8865ec31315d3c8398ec58cdb6665cb506a7d6309ebec3da0ca7dea84a5f406"
},
{
"oldAnchor": "verification-confidence-and-cognitive-load",
"title": "Verification confidence and cognitive load",
"docId": "measurement/verification-confidence-and-cognitive-load",
"url": "/strategy/measurement/verification-confidence-and-cognitive-load/",
"calculationId": "human-experience",
"status": "prototype",
"contractSha256": "01e017471004c5f780fd0c2671a4d4449d392956cb4c5f0bd7791959d6a9af9a"
},
{
"oldAnchor": "skills-within-substantive-review-window",
"title": "Skills within substantive review window",
"docId": "measurement/skills-within-substantive-review-window",
"url": "/strategy/measurement/skills-within-substantive-review-window/",
"calculationId": "maintenance-health",
"status": "unimplemented",
"contractSha256": "339fe0da6b382a34c52b5ec6005ce08a7293a2b07ccd88540a6df03a02d96ad4"
},
{
"oldAnchor": "workflow-specific-business-acceptance",
"title": "Workflow-specific business acceptance",
"docId": "measurement/workflow-specific-business-acceptance",
"url": "/strategy/measurement/workflow-specific-business-acceptance/",
"calculationId": "department-outcome",
"status": "unimplemented",
"contractSha256": "55fce8843409a1bc1383fef3cee5b9a616b44fa8d45ba84c6e4ed490a25f2de4"
}
],
"shared": [
{
"slug": "collection-and-safeguards",
"sha256": "37709cf9978a698026492b29f2095996c462e098a74f082a6b3c3a3a7b38ecab",
"previousSha256": "8610ed8df6a24cf138f9e6e2c09060db2aab4c0b1259ca0168e581e3fb964e52",
"revisionNote": "Stage 01 separation: baseline establishment moved to baseline.mdx; DORA candidates are optional. Event/privacy and outcome safeguards retained."
},
{
"slug": "synthetic-example",
"sha256": "24af68ff4fd61159d1c0557ed0f8f8462470d56c6c83816eb93b47fb04149fde"
}
]
}
+34
View File
@@ -0,0 +1,34 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync} from 'node:fs';
const read = path => readFileSync(path, 'utf8');
test('Baseline is workflow-scoped, evidence-bound and distinct from the optional registry', () => {
const page = read('docs/strategy/baseline.mdx');
for (const heading of ['Understand the current workflow','Select relevant metrics, with reasons','Agree feasible evidence','Record the starting position','Keep later evaluation comparable']) assert(page.includes(`## ${heading}`));
for (const text of ['not a mandatory company-wide baseline','no actual baseline values or approved thresholds','lawful basis','privacy','retention','source coverage','**Available:**','**Unavailable:**','not causal ROI','selected-but-unavailable']) assert(page.includes(text), text);
assert(page.indexOf('<ArchifyDiagram') < page.indexOf('## Understand'));
assert(!page.includes('25%'));
for (const doc of ['measurement/index','measurement/collection-and-safeguards','skills-adoption','skills-maintenance','skills-governance']) assert(page.includes(`./${doc}.mdx`));
const registry = read('docs/strategy/measurement/index.mdx');
assert(registry.includes('pagination_next: baseline'));
assert(page.includes('pagination_prev: measurement/index'));
assert(page.includes('pagination_next: skills-governance'));
});
test('workflow application has one home and shared safety remains explicit', () => {
const registry = read('docs/strategy/measurement/index.mdx');
const shared = read('docs/strategy/measurement/collection-and-safeguards.mdx');
const adoption = read('docs/strategy/skills-adoption.mdx');
const maintenance = read('docs/strategy/skills-maintenance.mdx');
assert(!registry.includes('Measure the pilot'));
assert(!shared.includes('Track all five'));
assert(!shared.includes('Before a pilot, freeze'));
assert(shared.includes('id') && shared.includes('{#baseline-and-interpretation}')); // Existing inbound anchor survives relocation.
for (const marker of ['transport retries','quarantined','Late arrivals','Unknown version stays null','30 days','12 months','below five people','No raw prompts','Consent is not presumed']) assert(shared.includes(marker),marker);
assert(adoption.includes('not a mandatory pilot bundle'));
assert(adoption.includes('What a pilot report looks like'));
assert(maintenance.includes('Keep selected evidence useful'));
assert(maintenance.includes('versioned corrections'));
assert(adoption.includes('**Next:** [Maintain skills using the agreed evidence →](./skills-maintenance.mdx)'));
});
+79
View File
@@ -0,0 +1,79 @@
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 prefix = '/eldorado-v1/documentation';
const manifest = JSON.parse(fs.readFileSync('research/ai-adoption/measurement-migration.json','utf8'));
const output = `.cache/metric-pages-${process.argv[3] || 'local'}`;
fs.mkdirSync(output,{recursive:true});
const browser = await chromium.launch({headless:true,executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']});
const results = [];
try {
for (const width of [1440,390]) {
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));
for (const m of manifest.metrics) {
const url = `${base}${prefix}${m.url}`;
assert.equal((await page.goto(url,{waitUntil:'networkidle'})).status(),200);
assert.equal(await page.locator('article h1').innerText(),m.title);
assert.equal(await page.locator('link[rel="canonical"]').getAttribute('href'),`https://pages.apps.lego-cloud.eu${prefix}${m.url}`);
const table = page.locator('article table');
assert.equal(await table.count(),1);
assert.equal(await table.locator('tbody tr').count(),11);
const article = await page.locator('article').innerText();
assert(article.includes(m.status === 'prototype' ? 'Prototype calculation' : 'Unimplemented metric'));
assert(article.includes('no live telemetry'));
assert.equal(await page.locator('.breadcrumbs a').filter({hasText:'Measurement'}).getAttribute('href'),`${prefix}/strategy/measurement/`);
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth));
await page.goto(`${base}${prefix}/strategy/reference/measurement-contracts/#${m.oldAnchor}`,{waitUntil:'networkidle'});
await page.waitForURL(url);
await page.locator('article h1').waitFor();
results.push({width,metric:m.oldAnchor,canonical:true,redirect:true,fields:11});
}
for (const suffix of ['', '#unknown-anchor', '#__proto__']) {
await page.goto(`${base}${prefix}/strategy/reference/measurement-contracts/${suffix}`,{waitUntil:'networkidle'});
await page.waitForURL(`${base}${prefix}/strategy/measurement/`);
}
for (const [anchor,target] of [
['collection-and-safeguards','collection-and-safeguards/'],
['decide-what-counts-before-counting','collection-and-safeguards/#decide-what-counts-before-counting'],
['baseline-and-interpretation','collection-and-safeguards/#baseline-and-interpretation'],
['event-and-privacy-contract','collection-and-safeguards/#event-and-privacy-contract'],
['run-the-synthetic-measurement-example','synthetic-example/'],
['metric-contracts','#metric-definitions'],
]) {
await page.goto(`${base}${prefix}/strategy/reference/measurement-contracts/#${anchor}`,{waitUntil:'networkidle'});
await page.waitForURL(`${base}${prefix}/strategy/measurement/${target}`);
if (target.includes('#')) await page.locator(`#${target.split('#')[1]}`).waitFor();
}
await page.goto(`${base}${prefix}/strategy/measurement/supported-bundle-coverage/`,{waitUntil:'networkidle'});
await page.locator('.breadcrumbs a').filter({hasText:'Measurement'}).click();
await page.waitForURL(`${base}${prefix}/strategy/measurement/`);
if (width < 997) await page.getByRole('button',{name:'Toggle navigation bar'}).click();
const menu = page.locator('.theme-doc-sidebar-menu').filter({visible:true});
const category = menu.locator('.theme-doc-sidebar-item-category').filter({has:page.getByRole('link',{name:'Measurement',exact:true})}).last();
const caret = category.locator(':scope > .menu__list-item-collapsible > button.menu__caret');
await caret.click();
await category.getByRole('link',{name:'Validated task success',exact:true}).waitFor({state:'hidden'});
await caret.focus(); await page.keyboard.press('Enter');
await category.getByRole('link',{name:'Validated task success',exact:true}).waitFor({state:'visible'});
await menu.getByRole('link',{name:'Measurement',exact:true}).click();
await page.waitForURL(`${base}${prefix}/strategy/measurement/`);
await page.locator('article h1').waitFor();
if (width < 997) await page.getByRole('button',{name:'Close navigation bar'}).waitFor({state:'hidden'});
await page.locator('#metric-definitions').scrollIntoViewIfNeeded();
await page.screenshot({path:`${output}/${width}-index.png`});
await page.goto(`${base}${prefix}/strategy/measurement/supported-bundle-coverage/`,{waitUntil:'networkidle'});
await page.screenshot({path:`${output}/${width}-metric.png`,fullPage:true});
assert.deepEqual(errors,[]);
} finally {await context.close();}
}
assert.equal(results.length,32);
fs.writeFileSync(`${output}/results.json`,JSON.stringify(results,null,2));
console.log('PASS: 16 metric pages × 2 widths; canonical metadata, contract fields, status, all legacy metric/shared/fallback redirects and linked Measurement keyboard navigation');
} finally {await browser.close();}
})().catch(e => {console.error(e);process.exitCode=1;});
+5 -4
View File
@@ -43,18 +43,19 @@ const fs = require('node:fs');
} }
const reference = '/eldorado-v1/documentation/strategy/reference/measurement-contracts/'; const reference = '/eldorado-v1/documentation/strategy/reference/measurement-contracts/';
await page.goto(`${base}${reference}#supported-bundle-coverage`, {waitUntil:'networkidle'}); await page.goto(`${base}${reference}#supported-bundle-coverage`, {waitUntil:'networkidle'});
await page.locator('#supported-bundle-coverage').waitFor(); await page.waitForURL(`${base}/eldorado-v1/documentation/strategy/measurement/supported-bundle-coverage/`);
await page.getByRole('heading', {level:1, name:'Supported bundle coverage', exact:true}).waitFor();
assert((await page.locator('article').innerText()).includes('Supported bundle coverage')); 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.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')); assert((await page.locator('.breadcrumbs').innerText()).includes('Measurement'));
await open(); await open();
await menu.getByRole('link', {name:'Measurement Contracts', exact:true}).waitFor({state:'visible'}); await menu.getByRole('link', {name:'Supported bundle coverage', exact:true}).waitFor({state:'visible'});
await page.screenshot({path:`${output}/${width}-reference.png`, animations:'disabled'}); await page.screenshot({path:`${output}/${width}-reference.png`, animations:'disabled'});
assert.deepEqual(errors, []); assert.deepEqual(errors, []);
} finally {await context.close();} } finally {await context.close();}
} }
assert.equal(results.length, documents.length * 3); assert.equal(results.length, documents.length * 3);
fs.writeFileSync(`${output}/results.json`, JSON.stringify(results,null,2)); 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`); console.log(`PASS: all ${documents.length} docs have visible unique sidebar entries at 3 viewports; Contribution links, Measurement breadcrumbs and reported anchor redirect work`);
} finally {await browser.close();} } finally {await browser.close();}
})().catch(e => {console.error(e); process.exitCode = 1;}); })().catch(e => {console.error(e); process.exitCode = 1;});
+45
View File
@@ -0,0 +1,45 @@
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 ['measurement','baseline','governance','quality','adoption','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.setViewportSize({width:spec.meta.viewBox[0],height:spec.meta.viewBox[1]});
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});
+8 -8
View File
@@ -18,11 +18,12 @@ const fs = require('node:fs');
assert.equal(await page.locator('h1').innerText(), 'Skills Measurement'); assert.equal(await page.locator('h1').innerText(), 'Skills Measurement');
assert((await page.title()).startsWith('Skills Measurement |')); assert((await page.title()).startsWith('Skills Measurement |'));
assert.equal(await page.locator('.strategy-content details').count(), 0); assert.equal(await page.locator('.strategy-content details').count(), 0);
assert.equal(await page.locator('.strategy-content h2').count(), 4); assert.equal(await page.locator('.strategy-content h2').count(), 2);
if (width < 700) await page.getByRole('button', {name:'Toggle navigation bar'}).click(); 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}); const menu = page.locator('.theme-doc-sidebar-menu .menu__link').filter({visible:true});
await menu.first().waitFor({state:'visible'}); await menu.first().waitFor({state:'visible'});
assert.deepEqual(await menu.allTextContents(), ['Target','Roadmap','Stage - 01 - Skills Adoption','Governance','Quality','Adoption','Measurement','Maintenance','Reference','Stage - 02 - Autonomous Agent']); const metrics = JSON.parse(fs.readFileSync('research/ai-adoption/measurement-migration.json','utf8')).metrics;
assert.deepEqual(await menu.allTextContents(), ['Target','Roadmap','Stage - 01 - Skills Adoption','Measurement',...metrics.map(m => m.title),'Collection and safeguards','Run the synthetic measurement example','Baseline','Governance','Quality','Adoption','Maintenance','Stage - 02 - Autonomous Agent']);
if (width < 700) await page.getByRole('button', {name:'Close navigation bar'}).click(); if (width < 700) await page.getByRole('button', {name:'Close navigation bar'}).click();
const panel = page.locator('.archify-panel--story'); const panel = page.locator('.archify-panel--story');
const iframe = panel.locator(width > 700 ? '.story-desktop' : '.story-mobile'); const iframe = panel.locator(width > 700 ? '.story-desktop' : '.story-mobile');
@@ -33,7 +34,7 @@ const fs = require('node:fs');
const nodes = [...svg.querySelectorAll('[data-node-id]')].map(n => n.getBoundingClientRect().toJSON()); 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))}; 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); 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}`); assert(geometry.minLabel >= 10, `readable node labels: ${geometry.minLabel}`);
await frame.locator('#btn-theme').click(); await frame.locator('#btn-theme').click();
@@ -50,7 +51,7 @@ const fs = require('node:fs');
assert.equal(await download.failure(), null); assert.equal(await download.failure(), null);
const path = `${output}/${width}.svg`; await download.saveAs(path); const path = `${output}/${width}.svg`; await download.saveAs(path);
const svg = fs.readFileSync(path, 'utf8'); const svg = fs.readFileSync(path, 'utf8');
assert(svg.includes('Establish baseline') && svg.includes('Ongoing monitoring')); assert(svg.includes('Available choices') && svg.includes('No mandatory collection'));
const opener = panel.getByRole('button', {name:/^Expand /}); const opener = panel.getByRole('button', {name:/^Expand /});
await opener.click(); await opener.click();
const close = panel.getByRole('button', {name:'Close full-screen diagram', exact:true}); const close = panel.getByRole('button', {name:'Close full-screen diagram', exact:true});
@@ -65,21 +66,20 @@ const fs = require('node:fs');
await page.locator('.strategy-content h2').nth(1).scrollIntoViewIfNeeded(); await page.locator('.strategy-content h2').nth(1).scrollIntoViewIfNeeded();
await page.screenshot({path:`${output}/${width}-pilot.png`}); await page.screenshot({path:`${output}/${width}-pilot.png`});
await page.screenshot({path:`${output}/${width}-full.png`, fullPage:true}); await page.screenshot({path:`${output}/${width}-full.png`, fullPage:true});
const links = await page.locator('.strategy-content a[href*="measurement-contracts"]').evaluateAll(nodes => nodes.map(n => n.href)); const links = await page.locator('.strategy-content a[href*="/measurement/"]').evaluateAll(nodes => nodes.map(n => n.href));
for (const href of [...new Set(links)]) { for (const href of [...new Set(links)]) {
const navigation = await page.goto(href, {waitUntil:'networkidle'}); const navigation = await page.goto(href, {waitUntil:'networkidle'});
if (navigation) assert.equal(navigation.status(), 200); if (navigation) assert.equal(navigation.status(), 200);
assert.equal(page.url(), href); assert.equal(page.url(), href);
if (new URL(href).hash) await page.locator(new URL(href).hash).waitFor({state:'visible'}); if (new URL(href).hash) await page.locator(new URL(href).hash).waitFor({state:'visible'});
} }
assert.equal(await page.locator('.strategy-content h3').count(), 19); assert.equal(new Set(links.map(h => new URL(h).pathname)).size, 18);
assert((await page.locator('.strategy-content').innerText()).includes('9 prototype calculations and 7 unimplemented metrics'));
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth)); assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth));
assert.deepEqual(errors, []); assert.deepEqual(errors, []);
results.push({width, geometry, links:links.length, title:true, sidebar:true, controls:true, export:true, reference:true}); results.push({width, geometry, links:links.length, title:true, sidebar:true, controls:true, export:true, reference:true});
fs.writeFileSync(`${output}/results.json`, JSON.stringify(results,null,2)); fs.writeFileSync(`${output}/results.json`, JSON.stringify(results,null,2));
await context.close(); await context.close();
} }
console.log(`PASS ${results.length} Skills Measurement viewports: title/sidebar, open plan, diagram geometry/controls/export, all reference links and no page errors`); console.log(`PASS ${results.length} Skills Measurement viewports: title/sidebar, optional registry, diagram geometry/controls/export, all reference links and no page errors`);
} finally {await browser.close();} } finally {await browser.close();}
})().catch(error => {console.error(error); process.exitCode = 1;}); })().catch(error => {console.error(error); process.exitCode = 1;});
+19 -6
View File
@@ -6,13 +6,23 @@ const assert=require('node:assert/strict');
const out=`.cache/skills-${process.argv[3]||'candidate'}`;fs.mkdirSync(out,{recursive:true}); 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=[]; const browser=await chromium.launch({executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE,args:['--no-sandbox']}); const results=[];
try { try {
for(const topic of ['governance','quality','adoption','measurement','maintenance']) for(const width of [1440,1024,390,320]) { for(const topic of ['measurement','baseline','governance','quality','adoption','maintenance']) for(const width of [1440,1024,390,320]) {
console.log(`Checking ${topic} at ${width}px`);
const page=await browser.newPage({viewport:{width,height:900},colorScheme:'light'});const errors=[];page.on('pageerror',e=>errors.push(e.message)); const 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}`; const route=['measurement','baseline'].includes(topic)?topic:`skills-${topic}`;
assert.equal((await page.goto(`${base}/eldorado-v1/documentation/strategy/${route}/`,{waitUntil:'networkidle'})).status(),200); 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 panel=page.locator('.archify-panel--skills');assert.equal(await panel.count(),1);
const iframe=panel.locator(width>700?'.story-desktop':'.story-mobile');await iframe.scrollIntoViewIfNeeded(); 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 frame=await(await iframe.elementHandle()).contentFrame();await frame.locator('svg[role="img"]').waitFor();
// Keep iframe controls below the fixed portal header. Frame-local click
// actionability cannot detect an overlay in the parent document.
const showControls=async()=>{
await panel.locator('dialog iframe').waitFor({state:'detached'});
await panel.evaluate(n=>{
scrollTo({top:n.getBoundingClientRect().top+scrollY-100,behavior:'instant'});
return new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)));
});
};
const geometry=await frame.locator('svg[role="img"]').evaluate(svg=>{ const geometry=await frame.locator('svg[role="img"]').evaluate(svg=>{
const nodes=[...svg.querySelectorAll('g[data-node-id]')];const issues=[];let min=Infinity; 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')){ for(const n of nodes){const rect=n.querySelector('rect').getBBox();for(const t of n.querySelectorAll('text')){
@@ -21,18 +31,21 @@ const assert=require('node:assert/strict');
}} }}
return {nodes:nodes.length,min,issues,width:innerWidth,svgWidth:svg.getBoundingClientRect().width}; return {nodes:nodes.length,min,issues,width:innerWidth,svgWidth:svg.getBoundingClientRect().width};
}); });
assert.equal(geometry.nodes,6);assert.deepEqual(geometry.issues,[]);assert(geometry.min>=10,JSON.stringify(geometry)); 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']){ for(const theme of ['light','dark']){
await showControls();
if(await frame.locator('html').getAttribute('data-theme')!==theme)await frame.locator('#btn-theme').click(); 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 page.waitForFunction(t=>document.documentElement.dataset.theme===t,theme).catch(error=>{throw new Error(`${topic}/${width}/${theme}: theme synchronization failed`,{cause:error});});
await panel.scrollIntoViewIfNeeded();await page.screenshot({path:`${out}/${topic}-${width}-${theme}-inline.png`}); 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 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(); const expanded=await(await panel.locator('dialog iframe').elementHandle()).contentFrame();await expanded.locator('svg[role="img"]').waitFor();
assert((await expanded.url()).includes(width<=700?`${topic}-mobile/`:`${topic}/`)); assert((await expanded.url()).includes(width<=1100?`${topic}-mobile/`:`${topic}/`));
await page.screenshot({path:`${out}/${topic}-${width}-${theme}-fullscreen.png`}); 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});} 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 close.focus();await page.keyboard.press('Escape');assert(await open.evaluate(n=>n===document.activeElement));
} }
await showControls();
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="blueprint"]').click();
await frame.locator('#btn-preset').click();await frame.locator('[data-preset-value="classic"]').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 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);
+17 -11
View File
@@ -8,9 +8,12 @@ const fs = require('node:fs');
const prefix = '/eldorado-v1/documentation/strategy/'; const prefix = '/eldorado-v1/documentation/strategy/';
const first = 'Stage - 01 - Skills Adoption'; const first = 'Stage - 01 - Skills Adoption';
const second = 'Stage - 02 - Autonomous Agent'; const second = 'Stage - 02 - Autonomous Agent';
const topics = ['Governance','Quality','Adoption','Measurement','Maintenance','Reference']; const topics = ['Measurement','Baseline','Governance','Quality','Adoption','Maintenance'];
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 metrics = JSON.parse(fs.readFileSync('research/ai-adoption/measurement-migration.json','utf8')).metrics;
const titles = ['Target','Roadmap',first,'Skills governance','Skills quality','Skills adoption','Skills Measurement','Skills maintenance','Skills KPI reference',second]; const childIds = [...metrics.map(m => m.docId),'measurement/collection-and-safeguards','measurement/synthetic-example'];
const childTitles = [...metrics.map(m => m.title),'Collection and safeguards','Run the synthetic measurement example'];
const ids = ['target-operating-model','roadmap','stage-01-skills-adoption','measurement',...childIds,'baseline','skills-governance','skills-quality','skills-adoption','skills-maintenance','stage-02-autonomous-agent'];
const titles = ['Target','Roadmap',first,'Skills Measurement',...childTitles,'Baseline','Skills governance','Skills quality','Skills adoption','Skills maintenance',second];
const browser = await chromium.launch({headless:true, executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, args:['--no-sandbox']}); const browser = await chromium.launch({headless:true, executablePath:process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, args:['--no-sandbox']});
const results = []; const results = [];
try { try {
@@ -39,11 +42,12 @@ const fs = require('node:fs');
await category.getByRole('link', {name:'Governance', exact:true}).waitFor({state:'visible'}); await category.getByRole('link', {name:'Governance', exact:true}).waitFor({state:'visible'});
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,second]); assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,second]);
assert.deepEqual(await category.locator('ul .menu__link').filter({visible:true}).allTextContents(), topics); assert.deepEqual(await category.locator('ul .menu__link').filter({visible:true}).allTextContents(), topics);
const reference = category.getByRole('button', {name:'Reference', exact:true}); const measurement = category.locator('.theme-doc-sidebar-item-category').filter({has:page.getByRole('link', {name:'Measurement', exact:true})});
const reference = measurement.locator(':scope > .menu__list-item-collapsible > button.menu__caret');
await reference.focus(); await reference.focus();
await page.keyboard.press('Enter'); await page.keyboard.press('Enter');
await category.getByRole('link', {name:'Measurement Contracts', exact:true}).waitFor({state:'visible'}); await category.getByRole('link', {name:'Validated task success', exact:true}).waitFor({state:'visible'});
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,'Measurement Contracts',second]); assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,topics[0],...childTitles,...topics.slice(1),second]);
await reference.click(); await reference.click();
await page.screenshot({path:`${output}/${width}-expanded.png`, animations:'disabled'}); await page.screenshot({path:`${output}/${width}-expanded.png`, animations:'disabled'});
await caret.click(); await caret.click();
@@ -64,14 +68,16 @@ const fs = require('node:fs');
else assert((await page.locator('h1').innerText()).includes('Target')); else assert((await page.locator('h1').innerText()).includes('Target'));
const prev = page.locator('.pagination-nav__link--prev'); const prev = page.locator('.pagination-nav__link--prev');
const next = page.locator('.pagination-nav__link--next'); const next = page.locator('.pagination-nav__link--next');
if (index) assert.equal(await prev.getAttribute('href'), `${prefix}${ids[index-1]}/`); if (index) assert.equal(await prev.getAttribute('href'), `${prefix}${id === 'baseline' ? 'measurement' : ids[index-1]}/`);
else assert.equal(await prev.count(), 0); else assert.equal(await prev.count(), 0);
if (index < ids.length-1) assert.equal(await next.getAttribute('href'), `${prefix}${ids[index+1]}/`); if (index < ids.length-1) assert.equal(await next.getAttribute('href'), `${prefix}${id === 'measurement' ? 'baseline' : ids[index+1]}/`);
else assert.equal(await next.count(), 0); else assert.equal(await next.count(), 0);
if (index >= 3 && index <= 7) { if (index >= 3 && index < ids.length - 1) {
assert.equal(await page.locator('.breadcrumbs a').filter({hasText:first}).getAttribute('href'), `${prefix}stage-01-skills-adoption/`); assert.equal(await page.locator('.breadcrumbs a').filter({hasText:first}).getAttribute('href'), `${prefix}stage-01-skills-adoption/`);
menu = await openMenu(); menu = await openMenu();
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...topics,second]); const expectedTopics = id === 'measurement' || id.startsWith('measurement/') ? [topics[0],...childTitles,...topics.slice(1)] : topics;
assert.deepEqual(await visibleLabels(), ['Target','Roadmap',first,...expectedTopics,second]);
if (id.startsWith('measurement/')) assert.equal(await page.locator('.breadcrumbs a').filter({hasText:'Measurement'}).getAttribute('href'), `${prefix}measurement/`);
await closeMenu(); await closeMenu();
} }
assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), `${width}: overflow on ${id}`); assert(!await page.evaluate(() => document.documentElement.scrollWidth > innerWidth), `${width}: overflow on ${id}`);
@@ -84,6 +90,6 @@ const fs = require('node:fs');
} finally {await context.close();} } finally {await context.close();}
} }
assert.equal(results.length, 4); 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'); console.log(`PASS: 4 desktop/mobile viewports; all ${ids.length} routes; linked Measurement category, keyboard expansion, breadcrumbs, next/previous and no overflow/errors`);
} finally {await browser.close();} } finally {await browser.close();}
})().catch(error => {console.error(error); process.exitCode = 1;}); })().catch(error => {console.error(error); process.exitCode = 1;});
+6 -3
View File
@@ -19,10 +19,12 @@ 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};}); 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))}; 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}`); 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})); 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); assert(data.words<=(id==='measurement'?1500:id==='roadmap'?600:200),id);assert(!data.overflow,id);assert.equal(data.openDetails,0,id);
await page.evaluate(()=>scrollTo({top:0,behavior:'instant'}));await page.screenshot({path:`.cache/story-${label}/${width}-${id}.png`,fullPage:true}); await page.evaluate(()=>scrollTo({top:0,behavior:'instant'}));await page.screenshot({path:`.cache/story-${label}/${width}-${id}.png`,fullPage:true});
const opener=panel.getByRole('button',{name:/^Expand /});await opener.click(); const opener=panel.getByRole('button',{name:/^Expand /});await opener.click();
const close=panel.getByRole('button',{name:'Close full-screen diagram',exact:true});await close.waitFor({state:'visible'}); const close=panel.getByRole('button',{name:'Close full-screen diagram',exact:true});await close.waitFor({state:'visible'});
@@ -42,7 +44,8 @@ const fs=require('node:fs');
results.push({id,width,...data,geometry,modalClick:true,modalEscape:true,focusRestored:true,details:id!=='measurement'});fs.writeFileSync(`.cache/story-${label}/results.json`,JSON.stringify(results,null,2)); results.push({id,width,...data,geometry,modalClick:true,modalEscape:true,focusRestored:true,details:id!=='measurement'});fs.writeFileSync(`.cache/story-${label}/results.json`,JSON.stringify(results,null,2));
} }
await page.goto(`${base}/eldorado-v1/documentation/strategy/reference/measurement-contracts/#validated-task-success`,{waitUntil:'networkidle'}); await page.goto(`${base}/eldorado-v1/documentation/strategy/reference/measurement-contracts/#validated-task-success`,{waitUntil:'networkidle'});
const heading=page.locator('#validated-task-success');await heading.waitFor({state:'visible'}); await page.waitForURL(`${base}/eldorado-v1/documentation/strategy/measurement/validated-task-success/`);
await page.getByRole('heading',{level:1,name:'Validated task success',exact:true}).waitFor();
assert.equal(await page.locator('.strategy-content details').count(),0,'reference is directly readable'); assert.equal(await page.locator('.strategy-content details').count(),0,'reference is directly readable');
await context.close(); await context.close();
} }
+38
View File
@@ -0,0 +1,38 @@
import assert from 'node:assert/strict';
import {readFileSync, readdirSync} from 'node:fs';
import {runInNewContext} from 'node:vm';
const read = p => readFileSync(p, 'utf8');
export function verifyMeasurementPages() {
const manifest = JSON.parse(read('research/ai-adoption/measurement-migration.json'));
const {documents, exclusions} = JSON.parse(read('.cache/sidebar-coverage.json'));
const prefix = '/eldorado-v1/documentation';
const legacy = 'strategy/reference/measurement-contracts/index.html';
assert.equal(exclusions.filter(e => e.file === legacy).length, 1);
assert(!documents.some(d => d.id === 'reference/measurement-contracts'));
const search = readdirSync('build').filter(f => /^search-index.*\.json$/.test(f)).map(f => read(`build/${f}`)).join('\n');
assert(!search.includes('/strategy/reference/measurement-contracts/'));
const children = [...manifest.metrics.map(m => ({...m,slug:m.oldAnchor})),
{slug:'collection-and-safeguards',title:'Collection and safeguards'},
{slug:'synthetic-example',title:'Run the synthetic measurement example'},
];
for (const child of children) {
const route = `${prefix}/strategy/measurement/${child.slug}/`;
const doc = documents.filter(d => `${d.route}/` === route);
assert.equal(doc.length,1);
assert.equal(doc[0].id, `measurement/${child.slug}`);
const page = read(`build/strategy/measurement/${child.slug}/index.html`);
const canonical = [...page.matchAll(/<link\b[^>]*rel="canonical"[^>]*>/g)];
assert.equal(canonical.length,1);
assert(canonical[0][0].includes(`href="https://pages.apps.lego-cloud.eu${route}"`));
assert(search.includes(route), `Missing search route: ${route}`);
assert(!page.includes(`${prefix}/strategy/reference/measurement-contracts/`));
}
const script = read(`build/${legacy}`).match(/<script>([\s\S]*?)<\/script>/)[1];
for (const m of manifest.metrics) {
let destination;
runInNewContext(script,{window:{location:{hash:`#${m.oldAnchor}`,replace:url => {destination=url;}}}});
assert.equal(destination,`${prefix}${m.url}`);
}
console.log('PASS: 18 canonical Measurement children, exact IDs, search membership and 16 hash redirects; no legacy document/index duplication');
}
+42
View File
@@ -0,0 +1,42 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync, existsSync, readdirSync} from 'node:fs';
import {createHash} from 'node:crypto';
import {calculate} from './ai-kpi.mjs';
const read = p => readFileSync(p, 'utf8');
test('each original metric survives exactly once on its own canonical Measurement page', () => {
const manifest = JSON.parse(read('research/ai-adoption/measurement-migration.json'));
const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
const report = calculate(JSON.parse(read('scripts/fixtures/ai-kpi-synthetic.json')));
assert.equal(manifest.metrics.length, 16);
assert.deepEqual(manifest.metrics.map(m => m.title), model.kpis.map(k => k.name));
assert.equal(new Set(manifest.metrics.map(m => m.docId)).size, 16);
assert.equal(manifest.metrics.filter(m => m.status === 'prototype').length, 9);
assert.equal(manifest.metrics.filter(m => m.status === 'unimplemented').length, 7);
const overview = read('docs/strategy/measurement/index.mdx');
for (const metric of manifest.metrics) {
const page = read(`docs/strategy/${metric.docId}.mdx`);
assert.equal(metric.url, `/strategy/${metric.docId}/`);
assert.equal(metric.oldAnchor, metric.docId.split('/').at(-1));
assert(page.includes(`slug: /${metric.docId}`));
assert(overview.includes(`./${metric.oldAnchor}.mdx`));
assert(page.includes('./collection-and-safeguards.mdx'));
assert(page.includes('not installed collection'));
assert(page.includes(metric.status === 'prototype' ? 'Prototype calculation' : 'Unimplemented metric'));
assert.equal(Object.hasOwn(report.kpis, metric.calculationId), metric.status === 'prototype');
const contract = page.split('{/* contract:start */}\n')[1]?.split('\n{/* contract:end */}')[0];
assert(contract, metric.title);
assert.equal(createHash('sha256').update(contract).digest('hex'), metric.contractSha256, metric.title);
for (const field of ['Numerator','Denominator','Expression','Aggregation','Unit','Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert.equal(contract.split(`| ${field} |`).length, 2, `${metric.title}: ${field}`);
}
assert.equal(readdirSync('docs/strategy/measurement').filter(p => p.endsWith('.mdx')).length, 19);
assert(!existsSync('docs/strategy/reference/measurement-contracts.mdx'));
for (const part of manifest.shared) {
const page = read(`docs/strategy/measurement/${part.slug}.mdx`);
const body = page.split('{/* preserved:start */}\n')[1]?.split('\n{/* preserved:end */}')[0];
// Only the cross-page navigation wording changes; hash all original substance.
const original = body.replace('Use the [metric definitions](./index.mdx#metric-definitions)', 'Use the metric contracts below');
assert.equal(createHash('sha256').update(original).digest('hex'), part.sha256);
}
});
+1 -1
View File
@@ -13,7 +13,7 @@ test('Roadmap has three visual levels and preserves human direction and Delivery
const visible = page.split('<details>')[0]; const visible = page.split('<details>')[0];
assert.deepEqual([...visible.matchAll(/<ArchifyDiagram name="([^"]+)"/g)].map(m => m[1]), Object.keys(views)); assert.deepEqual([...visible.matchAll(/<ArchifyDiagram name="([^"]+)"/g)].map(m => m[1]), Object.keys(views));
assert.deepEqual([...visible.matchAll(/^## (.+)$/gm)].map(m => m[1]), ['Two stages toward Target', 'Stage 1: Delivery first', 'Improve within each phase', 'Delivery in practice']); assert.deepEqual([...visible.matchAll(/^## (.+)$/gm)].map(m => m[1]), ['Two stages toward Target', 'Stage 1: Delivery first', 'Improve within each phase', 'Delivery in practice']);
for (const marker of ['Humans direct, guide and review throughout', 'standardizes skills', 'foundation for Stage 2', 'future Autonomous Agent', 'not deployed autonomy', 'order is not yet decided', 'Delivery keeps improving', 'not as six global roadmap stages', 'what skills exist, how they relate, when they are used and how they work together', 'codebase structure', 'existing principles', 'system-logic source', 'tests', 'builds and packaging', 'deployments', 'practices, tooling, controls and problems', 'metrics/KPIs and a baseline', 'friction and feedback', './measurement.mdx', './target-operating-model.mdx']) assert(visible.includes(marker), marker); for (const marker of ['Humans direct, guide and review throughout', 'standardizes skills', 'foundation for Stage 2', 'future Autonomous Agent', 'not deployed autonomy', 'order is not yet decided', 'Delivery keeps improving', 'not as six global roadmap stages', 'what skills exist, how they relate, when they are used and how they work together', 'codebase structure', 'existing principles', 'system-logic source', 'tests', 'builds and packaging', 'deployments', 'practices, tooling, controls and problems', 'metrics/KPIs and a baseline', 'friction and feedback', './measurement/index.mdx', './target-operating-model.mdx']) assert(visible.includes(marker), marker);
for (const marker of ['revocation','exact-artifact approvals','idempotency','human takeover','sensitive decisions','security findings','reproducible baseline']) assert(page.includes(marker), marker); for (const marker of ['revocation','exact-artifact approvals','idempotency','human takeover','sensitive decisions','security findings','reproducible baseline']) assert(page.includes(marker), marker);
assert(!/## [1-5]\.|Prepare foundations|Sustain and improve/.test(page)); assert(!/## [1-5]\.|Prepare foundations|Sustain and improve/.test(page));
}); });
+5
View File
@@ -82,6 +82,11 @@ export async function verifySidebarCoverage() {
else if (file === 'strategy/index.html') { else if (file === 'strategy/index.html') {
assert(readFileSync(join('build',file),'utf8').includes('http-equiv="refresh"'), 'Strategy alias must remain a redirect'); assert(readFileSync(join('build',file),'utf8').includes('http-equiv="refresh"'), 'Strategy alias must remain a redirect');
reason = 'Legacy Strategy redirect alias to Target'; reason = 'Legacy Strategy redirect alias to Target';
} else if (file === 'strategy/reference/measurement-contracts/index.html') {
const body = readFileSync(join('build',file),'utf8');
assert(body.includes('window.location.replace') && body.includes('name="robots" content="noindex"') && !body.includes('<article'), 'Metric alias must remain a non-indexed redirect');
assert.equal(body, readFileSync(join('static',file),'utf8'));
reason = 'Legacy measurement-contracts hash-aware redirect to canonical Measurement pages';
} else if (file.startsWith('diagrams/') && readFileSync(join('build',file),'utf8').includes('<svg')) { } 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}`); 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'; reason = 'Standalone diagram viewer asset, not documentation';
+11 -8
View File
@@ -4,21 +4,24 @@ import {readFileSync} from 'node:fs';
import {calculate} from './ai-kpi.mjs'; import {calculate} from './ai-kpi.mjs';
const read = path => readFileSync(path, 'utf8'); const read = path => readFileSync(path, 'utf8');
test('Skills Measurement is an open adoption collection plan, not a hidden catalogue', () => { test('Skills Measurement is a visible optional registry, separate from workflow application', () => {
const page = read('docs/strategy/measurement.mdx'); const page = read('docs/strategy/measurement/index.mdx');
assert.match(page, /title: Skills Measurement\nsidebar_label: 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); for (const heading of ['Establish the baseline', 'Measure the pilot', 'Extend collection as adoption grows', 'Keep the collection running']) assert(!page.includes(heading), heading);
assert(page.includes('Registry inclusion does not require a metric to be used, collected or baselined'));
assert(page.includes('../baseline.mdx'));
assert(!page.includes('<details>')); assert(!page.includes('<details>'));
assert(page.includes('./reference/measurement-contracts.mdx')); assert(page.includes('./collection-and-safeguards.mdx'));
assert(!page.includes('Is AI-assisted work worth continuing?')); assert(!page.includes('Is AI-assisted work worth continuing?'));
for (const word of ['Recommended', 'not installed', 'unknown', 'review', 'CI', 'revision', 'Monthly', 'Weekly']) assert(page.includes(word), word); for (const word of ['candidate', 'not installed', 'applicability', 'calculation', 'inputs', 'limitations', 'implementation status']) assert(page.includes(word), word);
}); });
test('all sixteen metric definitions remain in the reference with explicit implementation limits', () => { test('all sixteen metric definitions remain in the reference with explicit implementation limits', () => {
const page = read('docs/strategy/reference/measurement-contracts.mdx'); const manifest = JSON.parse(read('research/ai-adoption/measurement-migration.json'));
const page = read('docs/strategy/measurement/index.mdx') + read('docs/strategy/measurement/collection-and-safeguards.mdx');
const model = JSON.parse(read('research/ai-adoption/strategy-model.json')); const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
for (const k of model.kpis) { for (const k of model.kpis) {
const section = page.split(`### ${k.name}\n`)[1]?.split('\n### ')[0]; const section = read(`docs/strategy/${manifest.metrics.find(m => m.title === k.name).docId}.mdx`);
assert(section, k.name); assert(section, k.name);
for (const field of ['Numerator','Denominator','Expression','Aggregation','Unit','Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${field} |`)); for (const field of ['Numerator','Denominator','Expression','Aggregation','Unit','Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${field} |`));
} }
@@ -26,7 +29,7 @@ test('all sixteen metric definitions remain in the reference with explicit imple
}); });
test('published pilot arithmetic matches the executable fixture, including partial cost', () => { test('published pilot arithmetic matches the executable fixture, including partial cost', () => {
const page = read('docs/strategy/measurement.mdx'); const page = read('docs/strategy/skills-adoption.mdx');
const fixture = JSON.parse(read('scripts/fixtures/ai-kpi-synthetic.json')); const fixture = JSON.parse(read('scripts/fixtures/ai-kpi-synthetic.json'));
const report = calculate(fixture); const report = calculate(fixture);
const success = report.kpis['validated-task-success']; const success = report.kpis['validated-task-success'];
+25 -12
View File
@@ -6,43 +6,56 @@ import {join} from 'node:path';
import {execFileSync} from 'node:child_process'; import {execFileSync} from 'node:child_process';
import {skillsViewer,isSkillsStory} from './skills-viewer.mjs'; import {skillsViewer,isSkillsStory} from './skills-viewer.mjs';
const read = p => readFileSync(p,'utf8'); const read = p => readFileSync(p,'utf8');
const topics = ['governance','quality','adoption','measurement','maintenance']; const topics = ['measurement','baseline','governance','quality','adoption','maintenance'];
test('all six Skills Adoption pages account for exactly five diagrams and ten sources',()=>{ test('all seven Skills Adoption pages account for six diagrams and twelve sources',()=>{
let count=0; let count=0;
for(const route of ['stage-01-skills-adoption',...topics.map(t=>t==='measurement'?t:`skills-${t}`)]){ for(const route of ['stage-01-skills-adoption',...topics.map(t=>['measurement','baseline'].includes(t)?t:`skills-${t}`)]){
const diagrams=[...read(`docs/strategy/${route}.mdx`).matchAll(/<ArchifyDiagram name="([^"]+)"/g)]; const diagrams=[...read(`docs/strategy/${route === 'measurement' ? 'measurement/index' : route}.mdx`).matchAll(/<ArchifyDiagram name="([^"]+)"/g)];
assert.equal(diagrams.length,route.startsWith('stage-')?0:1); assert.equal(diagrams.length,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++;} 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')); assert.equal(count,6);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`,()=>{ 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 name=`strategy-${topic}`;const desktop=JSON.parse(read(`diagrams/${name}.architecture.json`));
const mobile=JSON.parse(read(`diagrams/${name}-mobile.architecture.json`)); const mobile=JSON.parse(read(`diagrams/${name}-mobile.architecture.json`));
const semantic = d=>d.components.map(({pos,size,...n})=>n); const semantic = d=>d.components.map(({pos,size,...n})=>n);
assert.deepEqual(semantic(desktop),semantic(mobile)); assert.deepEqual(semantic(desktop),semantic(mobile));
assert.equal(new Set(desktop.components.map(n=>n.pos[0])).size,3); 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); 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.split('\n').length>=4);} 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-')); const dir=mkdtempSync(join(tmpdir(),'skills-viewer-'));
try{ try{
const path=join(dir,'native.html'); const path=join(dir,'native.html');
execFileSync(process.execPath,['vendor/archify/renderers/architecture/render-architecture.mjs',`diagrams/${name}.architecture.json`,path]); execFileSync(process.execPath,['vendor/archify/renderers/architecture/render-architecture.mjs',`diagrams/${name}.architecture.json`,path]);
const native=read(path);const html=skillsViewer(native,desktop); const native=read(path);const html=skillsViewer(native,desktop);
assert.equal((html.match(/<g\b[^>]*data-node-id=/g)||[]).length,6); 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(/<text data-node-label=/g)||[]).length,0); // Adapter uses explicit position before semantic attributes.
assert.equal((html.match(/data-node-label=""/g)||[]).length,6); assert.equal((html.match(/data-node-label=""/g)||[]).length,desktop.components.length);
assert(html.includes('eldorado-skills-typography')); assert(html.includes('eldorado-skills-typography'));
assert.throws(()=>skillsViewer(native.replace('data-node-id="'+desktop.components[0].id+'"','data-node-id="missing"'),desktop)); 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='not a full story';assert.throws(()=>skillsViewer(native,bad)); const bad=structuredClone(desktop);bad.components[0].sublabel='';assert.throws(()=>skillsViewer(native,bad));
}finally{rmSync(dir,{recursive:true,force:true});} }finally{rmSync(dir,{recursive:true,force:true});}
}); });
test('domain contracts retain human authority, honest measures and Delivery-first rollout',()=>{ test('domain contracts retain human authority, honest measures and Delivery-first rollout',()=>{
const text=t=>read(`diagrams/strategy-${t}.architecture.json`); const text=t=>JSON.stringify(JSON.parse(read(`diagrams/strategy-${t}.architecture.json`)));
assert(text('governance').includes('Author is not sole approver')); assert(text('governance').includes('Author is not sole approver'));
assert(text('quality').includes('Critical effect blocks')); assert(text('quality').includes('Critical effect blocks'));
assert(text('adoption').includes('Delivery first')&&text('adoption').includes('Later order undecided')); assert(text('adoption').includes('Delivery first')&&text('adoption').includes('Later order undecided'));
assert(text('adoption').includes('No quotas or rankings')); assert(text('adoption').includes('No quotas or rankings'));
for(const marker of ['Tracker:','Invoices + review minutes','linked defect records','Anonymous 1–5 survey','Missing buckets = partial','Zero denominator = N/A'])assert(text('measurement').includes(marker),marker); for(const marker of ['Available choices','16 candidate contracts','No mandatory collection','Meaning + applicability','9 prototype / 7 unimplemented'])assert(text('measurement').includes(marker),marker);
assert(text('maintenance').includes('No automatic reinstatement')); 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 ['qualification','outcomes','experience','health']){has(m,'registry',id);has(m,id,'contract');}
const b=spec('baseline');for(const [x,y] of [['workflow','selection'],['selection','evidence'],['evidence','position'],['position','comparison']])has(b,x,y);
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);
});
+19 -10
View File
@@ -1,8 +1,9 @@
// Keep native geometry, metadata, routing and exports; enrich only typography. // Native editable geometry and edges; scoped editorial typography for Stage 01.
export const isSkillsStory = name => /^strategy-(governance|quality|adoption|measurement|maintenance)(-mobile)?$/.test(name); export const isSkillsStory = name => /^strategy-(governance|quality|adoption|measurement|baseline|maintenance)(-mobile)?$/.test(name);
const escape = text => text.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;'); const escape = text => text.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;');
export function skillsViewer(html, spec) { export function skillsViewer(html, spec) {
if (spec.diagram_type !== 'architecture' || spec.components.length !== 6) throw new Error('Skills story contract drift'); 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) { 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 pattern = new RegExp(`<g\\b[^>]*data-node-id="${node.id}"[^>]*>[\\s\\S]*?<text data-node-label=[\\s\\S]*?</g>`);
const match = html.match(pattern); const match = html.match(pattern);
@@ -12,24 +13,32 @@ export function skillsViewer(html, spec) {
if (rectangles.length !== 2) throw new Error('Skills shape contract drift'); if (rectangles.length !== 2) throw new Error('Skills shape contract drift');
const [x,y] = node.pos; const [x,y] = node.pos;
const lines = node.sublabel.split('\n'); const lines = node.sublabel.split('\n');
if ((lines.length < 4 || lines.length > 5) || lines.some(line => line.length > 29) || node.label.length > 25) throw new Error(`Skills copy exceeds capacity: ${node.id}`); 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 text = (value, top, size, attrs) => `<text x="${x+28}" y="${y+top}" font-size="${size}" ${attrs}>${escape(value)}</text>`;
const content = text(node.tag, 52, 36, 'class="t-muted" data-detail="context"') const content = text(node.tag, 40, detailSize, 'class="t-muted" data-detail="context"')
+ text(node.label, 118, 42, 'class="t-primary" data-node-label="" font-weight="700"') + text(node.label, 89, 36, 'class="t-primary" data-node-label="" font-weight="700"')
+ lines.map((line,i) => text(line, 190+i*46, 36, 'class="t-primary" data-detail="context"')).join(''); + 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"'); const prefix = original.slice(0,rectangles[1].index+rectangles[1][0].length).replaceAll('rx="6"','rx="0"');
html = html.replace(original,prefix+content+'</g>'); html = html.replace(original,prefix+content+'</g>');
} }
const roots = [...html.matchAll(/<svg\b[^>]*role="img"[^>]*>/g)]; const roots = [...html.matchAll(/<svg\b[^>]*role="img"[^>]*>/g)];
if (roots.length !== 1) throw new Error('Skills SVG contract drift'); if (roots.length !== 1) throw new Error('Skills SVG contract drift');
const width = Math.max(...spec.components.map(n=>n.pos[0]+n.size[0]))+40; const [width,height] = spec.meta.viewBox;
const height = Math.max(...spec.components.map(n=>n.pos[1]+n.size[1]))+40;
const root = roots[0][0].replace(/viewBox="[^"]*"/,`viewBox="0 0 ${width} ${height}"`); const root = roots[0][0].replace(/viewBox="[^"]*"/,`viewBox="0 0 ${width} ${height}"`);
// Embedded in SVG so downloaded files retain typography without the viewer. // Embedded in SVG so downloaded files retain typography without the viewer.
html = html.replace(roots[0][0],root+`<style id="eldorado-skills-typography"> html = html.replace(roots[0][0],root+`<style id="eldorado-skills-typography">
[data-node-id] text {font-family:Arial,sans-serif;} [data-node-id] text {font-family:Arial,sans-serif;}
.c-grid {opacity:0;} rect[fill="url(#grid)"] {opacity:0;}
[data-node-id] > rect:not(.c-mask) {stroke-width:3;} [data-node-id] > rect:not(.c-mask) {stroke-width:3;}
[data-edge-id] {opacity:1;}
</style>`); </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; return html;
} }
+8 -4
View File
@@ -14,12 +14,16 @@ test('Strategy has Target, Roadmap, a linked Skills Adoption category and Autono
type:'category', label:'Stage - 01 - Skills Adoption', type:'category', label:'Stage - 01 - Skills Adoption',
link:{type:'doc', id:'stage-01-skills-adoption'}, collapsed:true, link:{type:'doc', id:'stage-01-skills-adoption'}, collapsed:true,
items:[ items:[
{type:'category', label:'Measurement', collapsed:true, link:{type:'doc', id:'measurement/index'}, items:[
...JSON.parse(read('research/ai-adoption/measurement-migration.json')).metrics.map(m => m.docId),
'measurement/collection-and-safeguards','measurement/synthetic-example',
]},
{type:'doc', id:'baseline', label:'Baseline'},
{type:'doc', id:'skills-governance', label:'Governance'}, {type:'doc', id:'skills-governance', label:'Governance'},
{type:'doc', id:'skills-quality', label:'Quality'}, {type:'doc', id:'skills-quality', label:'Quality'},
{type:'doc', id:'skills-adoption', label:'Adoption'}, {type:'doc', id:'skills-adoption', label:'Adoption'},
{type:'doc', id:'measurement', label:'Measurement'},
{type:'doc', id:'skills-maintenance', label:'Maintenance'}, {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']); }, 'stage-02-autonomous-agent']);
assert.match(read('docs/strategy/target-operating-model.mdx'), /^sidebar_label: Target$/m); assert.match(read('docs/strategy/target-operating-model.mdx'), /^sidebar_label: Target$/m);
@@ -36,10 +40,10 @@ test('stage overviews explain the foundation and future without replacing topic
assert(page.includes(`id: ${id}\ntitle: ${title}`)); assert(page.includes(`id: ${id}\ntitle: ${title}`));
for (const link of ['./roadmap.mdx', './target-operating-model.mdx']) assert(page.includes(link)); 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 id of ['measurement/index','baseline','skills-governance','skills-quality','skills-adoption','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 ['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); 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); assert.match(read('docs/strategy/measurement/index.mdx'), /^title: Skills Measurement\nsidebar_label: Measurement$/m);
}); });
test('old Strategy entry redirects to Target instead of maintaining a second document', () => { test('old Strategy entry redirects to Target instead of maintaining a second document', () => {
+11 -11
View File
@@ -6,25 +6,25 @@ const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
test('every strategy page leads with a compact diagram story and closes its references', () => { test('every strategy page leads with a compact diagram story and closes its references', () => {
for (const {id} of model.pages) { for (const {id} of model.pages) {
const page = read(`docs/strategy/${id}.mdx`); const page = read(`docs/strategy/${id === 'measurement' ? 'measurement/index' : id}.mdx`);
const narrative = page.split('<div className="strategy-content">')[1].replace(/<details>[\s\S]*?<\/details>/g, ''); const narrative = page.split('<div className="strategy-content">')[1].replace(/<details>[\s\S]*?<\/details>/g, '');
assert.equal((narrative.match(/<ArchifyDiagram /g) || []).length, id === 'roadmap' ? 3 : 1, id); assert.equal((narrative.match(/<ArchifyDiagram /g) || []).length, id === 'roadmap' ? 3 : 1, id);
assert(narrative.includes(' story />'), id); assert(narrative.includes(' story />'), id);
assert(!page.includes('<details>') || page.indexOf(' story />') < page.indexOf('<details>'), id); assert(!page.includes('<details>') || page.indexOf(' story />') < page.indexOf('<details>'), id);
assert(!/<details\s+open/.test(page), id); assert(!/<details\s+open/.test(page), id);
assert.equal((page.match(/<details>/g) || []).length, (page.match(/<\/details>/g) || []).length, id); assert.equal((page.match(/<details>/g) || []).length, (page.match(/<\/details>/g) || []).length, id);
assert.equal((narrative.match(/^## /gm) || []).length, id === 'measurement' ? 4 : id === 'roadmap' ? 4 : 1, id); assert.equal((narrative.match(/^## /gm) || []).length, id === 'measurement' ? 2 : id === 'roadmap' ? 4 : 1, id);
assert((narrative.match(/^- /gm) || []).length <= 3, id); assert((narrative.match(/^- /gm) || []).length <= 3, id);
assert(narrative.includes('**Next:**'), id); assert(narrative.includes('**Next:**'), id);
assert(!narrative.includes('| ---'), id); assert(!narrative.includes('| ---'), id);
const words = narrative.replace(/<[^>]*>/g, '').trim().split(/\s+/).length; const words = narrative.replace(/<[^>]*>/g, '').trim().split(/\s+/).length;
assert(words <= (id === 'measurement' ? 1100 : id === 'roadmap' ? 600 : 200), `${id}: ${words} default narrative words`); assert(words <= (id === 'measurement' ? 1500 : id === 'roadmap' ? 600 : 200), `${id}: ${words} default narrative words`);
} }
}); });
test('mobile story diagrams preserve the same nodes and directed relationships', () => { test('mobile story diagrams preserve the same nodes and directed relationships', () => {
for (const {id} of model.pages) { for (const {id} of model.pages) {
const page = read(`docs/strategy/${id}.mdx`); const page = read(`docs/strategy/${id === 'measurement' ? 'measurement/index' : id}.mdx`);
const name = /<ArchifyDiagram name="([^"]+)"[^>]+ story/.exec(page)[1]; const name = /<ArchifyDiagram name="([^"]+)"[^>]+ story/.exec(page)[1];
if (name === 'strategy-roadmap') { if (name === 'strategy-roadmap') {
const desktop = JSON.parse(read(`diagrams/${name}.workflow.json`)); const desktop = JSON.parse(read(`diagrams/${name}.workflow.json`));
@@ -35,21 +35,21 @@ test('mobile story diagrams preserve the same nodes and directed relationships',
} }
const desktop = JSON.parse(read(`diagrams/${name}.architecture.json`)); const desktop = JSON.parse(read(`diagrams/${name}.architecture.json`));
const mobile = JSON.parse(read(`diagrams/${name}-mobile.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)}); 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.deepEqual(semantic(desktop), semantic(mobile), name);
assert(desktop.connections.every(e => (e.label || e.id.startsWith('govern-') || /^strategy-(governance|quality|adoption|maintenance)$/.test(name)) && 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'); assert.equal(desktop.meta.legend.mode, 'hidden');
} }
}); });
test('all sixteen exact KPI formulas remain reachable in the linked reference', () => { test('all sixteen exact KPI formulas remain reachable in the linked reference', () => {
assert(read('docs/strategy/measurement.mdx').includes('./reference/measurement-contracts.mdx')); const manifest = JSON.parse(read('research/ai-adoption/measurement-migration.json'));
const page = read('docs/strategy/reference/measurement-contracts.mdx'); assert.equal(manifest.metrics.length, 16);
const contracts = page.split('## Metric contracts')[1].split('## Run the synthetic')[0];
assert.equal((contracts.match(/^### /gm) || []).length, 16);
for (const metric of model.kpis) { for (const metric of model.kpis) {
const section = contracts.split(`### ${metric.name}\n`)[1]?.split('\n### ')[0]; const entry = manifest.metrics.find(m => m.title === metric.name);
assert(read('docs/strategy/measurement/index.mdx').includes(`./${entry.oldAnchor}.mdx`));
const section = read(`docs/strategy/${entry.docId}.mdx`);
assert(section, metric.name); assert(section, metric.name);
for (const label of ['Numerator','Denominator','Expression','Aggregation','Unit','Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${label} |`), `${metric.name}: ${label}`); for (const label of ['Numerator','Denominator','Expression','Aggregation','Unit','Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${label} |`), `${metric.name}: ${label}`);
} }
+7 -7
View File
@@ -6,13 +6,13 @@ const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
test('seven existing strategy pages and two stage overviews preserve canonical routes', () => { test('seven existing strategy pages and two stage overviews preserve canonical routes', () => {
assert.equal(model.pages.length, 7); assert.equal(model.pages.length, 7);
assert.deepEqual(readdirSync('docs/strategy').filter(p => p.endsWith('.mdx')).sort(), 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()); [...model.pages.filter(p => p.id !== 'measurement').map(({id}) => `${id}.mdx`), 'stage-01-skills-adoption.mdx', 'stage-02-autonomous-agent.mdx', 'baseline.mdx'].sort());
for (const {id} of model.pages) { for (const {id} of model.pages) {
const page = read(`docs/strategy/${id}.mdx`); const page = read(`docs/strategy/${id === 'measurement' ? 'measurement/index' : id}.mdx`);
assert(page.includes(`id: ${id}`)); assert(page.includes(`id: ${id === 'measurement' ? 'index' : id}`));
assert(page.includes('Target operating model')); assert(page.includes('Target operating model'));
assert(!/## Sources|\[\d+\]|Internal evidence mapping/.test(page)); assert(!/## Sources|\[\d+\]|Internal evidence mapping/.test(page));
assert(read('sidebars/strategy.ts').includes(`'${id}'`)); assert(read('sidebars/strategy.ts').includes(`'${id === 'measurement' ? 'measurement/index' : id}'`));
} }
for (const teams of Object.values(model.organization)) for (const name of teams) { for (const teams of Object.values(model.organization)) for (const name of teams) {
assert(read('docs/organization/structure.mdx').includes(name)); assert(read('docs/organization/structure.mdx').includes(name));
@@ -20,15 +20,15 @@ test('seven existing strategy pages and two stage overviews preserve canonical r
} }
}); });
test('every metric has an actionable reader-facing contract', () => { test('every metric has an actionable reader-facing contract', () => {
const page = read('docs/strategy/reference/measurement-contracts.mdx'); const manifest = JSON.parse(read('research/ai-adoption/measurement-migration.json'));
assert.equal(model.kpis.length,16); assert.equal(model.kpis.length,16);
for (const k of model.kpis) { for (const k of model.kpis) {
const section=page.split(`### ${k.name}\n`)[1]?.split('\n### ')[0]; const section=read(`docs/strategy/${manifest.metrics.find(m => m.title === k.name).docId}.mdx`);
assert(section,k.name); assert(section,k.name);
for (const field of ['Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${field} |`)); for (const field of ['Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${field} |`));
assert(section.includes(k.owner)); assert(section.includes(k.owner));
} }
assert(page.includes('synthetic') && page.includes('not a production ingestion system')); assert(read('docs/strategy/measurement/synthetic-example.mdx').includes('not a production ingestion system'));
}); });
test('diagram preserves independent release approval and no agent production authority', () => { test('diagram preserves independent release approval and no agent production authority', () => {
const d=JSON.parse(read('diagrams/ai-adoption-target.architecture.json')); const d=JSON.parse(read('diagrams/ai-adoption-target.architecture.json'));
+4
View File
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
import {readFileSync, readdirSync, existsSync} from 'node:fs'; import {readFileSync, readdirSync, existsSync} from 'node:fs';
import {join} from 'node:path'; import {join} from 'node:path';
import {verifySidebarCoverage} from './sidebar-coverage.mjs'; import {verifySidebarCoverage} from './sidebar-coverage.mjs';
import {verifyMeasurementPages} from './measurement-pages.mjs';
const read = p => readFileSync(p, 'utf8'); const read = p => readFileSync(p, 'utf8');
const config = read('docusaurus.config.ts'); const config = read('docusaurus.config.ts');
assert(config.includes("baseUrl: '/eldorado-v1/documentation/'")); assert(config.includes("baseUrl: '/eldorado-v1/documentation/'"));
@@ -18,13 +19,16 @@ assert(workflow.includes('mountpoint -q'));
assert(workflow.includes("gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'")); assert(workflow.includes("gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'"));
if (process.argv.includes('--build')) { if (process.argv.includes('--build')) {
await verifySidebarCoverage(); await verifySidebarCoverage();
verifyMeasurementPages();
const model = JSON.parse(read('research/ai-adoption/strategy-model.json')); 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 indexes = readdirSync('build').filter(f => /^search-index.*\.json$/.test(f)).map(f => read(join('build',f))).join('\n');
assert(indexes.includes('/eldorado-v1/documentation/strategy/baseline/'), 'Missing indexed Baseline route');
const redirect = read('build/strategy/index.html'); const redirect = read('build/strategy/index.html');
assert(redirect.includes('/eldorado-v1/documentation/strategy/target-operating-model/')); 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'); assert(redirect.includes('window.location.href') && redirect.includes('http-equiv="refresh"') && !redirect.includes('archify-panel'), 'Landing must redirect, not duplicate Target');
const strategyPages = [...model.pages, const strategyPages = [...model.pages,
{id:'stage-01-skills-adoption', title:'Stage - 01 - Skills Adoption'}, {id:'stage-01-skills-adoption', title:'Stage - 01 - Skills Adoption'},
{id:'baseline', title:'Baseline'},
{id:'stage-02-autonomous-agent', title:'Stage - 02 - Autonomous Agent'}, {id:'stage-02-autonomous-agent', title:'Stage - 02 - Autonomous Agent'},
]; ];
for (const {id, title} of strategyPages) { for (const {id, title} of strategyPages) {
+25 -5
View File
@@ -9,15 +9,35 @@ export default {
link: {type: 'doc', id: 'stage-01-skills-adoption'}, link: {type: 'doc', id: 'stage-01-skills-adoption'},
collapsed: true, collapsed: true,
items: [ items: [
{
type: 'category', label: 'Measurement', collapsed: true,
link: {type: 'doc', id: 'measurement/index'},
items: [
'measurement/validated-task-success',
'measurement/skill-selection-precision',
'measurement/skill-selection-recall',
'measurement/supported-bundle-coverage',
'measurement/approved-workflow-adoption',
'measurement/change-lead-time',
'measurement/deployment-frequency',
'measurement/change-fail-rate',
'measurement/failed-deployment-recovery-time',
'measurement/deployment-rework-rate',
'measurement/changes-with-escaped-quality-issues',
'measurement/confirmed-policy-effects',
'measurement/cost-per-accepted-outcome',
'measurement/verification-confidence-and-cognitive-load',
'measurement/skills-within-substantive-review-window',
'measurement/workflow-specific-business-acceptance',
'measurement/collection-and-safeguards',
'measurement/synthetic-example',
],
},
{type: 'doc', id: 'baseline', label: 'Baseline'},
{type: 'doc', id: 'skills-governance', label: 'Governance'}, {type: 'doc', id: 'skills-governance', label: 'Governance'},
{type: 'doc', id: 'skills-quality', label: 'Quality'}, {type: 'doc', id: 'skills-quality', label: 'Quality'},
{type: 'doc', id: 'skills-adoption', label: 'Adoption'}, {type: 'doc', id: 'skills-adoption', label: 'Adoption'},
{type: 'doc', id: 'measurement', label: 'Measurement'},
{type: 'doc', id: 'skills-maintenance', label: 'Maintenance'}, {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', 'stage-02-autonomous-agent',
+2 -2
View File
@@ -12,10 +12,10 @@ export default function ArchifyDiagram({name, title, compact = false, story = fa
const headingId = useId(); const headingId = useId();
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
const [narrowRoadmap, setNarrowRoadmap] = useState(false); const [narrowRoadmap, setNarrowRoadmap] = useState(false);
const skillsStory = /^strategy-(governance|quality|adoption|measurement|maintenance)$/.test(name); const skillsStory = /^strategy-(governance|quality|adoption|measurement|baseline|maintenance)$/.test(name);
useEffect(() => { useEffect(() => {
if (!name.startsWith('strategy-roadmap') && !skillsStory) return; if (!name.startsWith('strategy-roadmap') && !skillsStory) return;
const media = window.matchMedia(skillsStory ? '(max-width: 700px)' : '(max-width: 999px)'); const media = window.matchMedia(skillsStory ? '(max-width: 1100px)' : '(max-width: 999px)');
const update = () => setNarrowRoadmap(media.matches); const update = () => setNarrowRoadmap(media.matches);
update(); update();
media.addEventListener('change', update); media.addEventListener('change', update);
+13 -11
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"] {--roadmap-height: 1840px;}
.archify-panel--story[data-diagram="strategy-roadmap-loop"] > .story-desktop {height: 460px;} .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-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 > 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 {cursor: pointer; font-weight: 700;}
.strategy-content summary:focus-visible {outline: 3px solid #b88719; outline-offset: 4px;} .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-desktop {display: none;}
.archify-panel--story > .story-mobile {display: block; height: auto; aspect-ratio: 240 / 748;} .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;} .archify-panel--story[data-diagram="strategy-target"] > .story-mobile {aspect-ratio: 680 / 2340;}
} }
/* Keep the three-card loop readable beside the documentation sidebar. */ /* Keep the three-card loop readable beside the documentation sidebar. */
@@ -156,13 +154,17 @@ a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offse
/* Skills Adoption alone uses the full reading column, not a narrow preview. */ /* Skills Adoption alone uses the full reading column, not a narrow preview. */
.theme-doc-markdown:has(.archify-panel--skills) {max-width: none;} .theme-doc-markdown:has(.archify-panel--skills) {max-width: none;}
.archify-panel--story.archify-panel--skills {max-width: none;} .archify-panel--story.archify-panel--skills {max-width: none;}
.archify-panel--story.archify-panel--skills > .story-desktop {aspect-ratio: auto; min-height: 460px; height: calc((100vw - 364px) * .56 + 72px); max-height: 720px;} .archify-panel--skills {--skills-wide: 2000 / 1200; --skills-narrow: 880 / 2900;}
@media (max-width: 996px) and (min-width: 701px) { .archify-panel--skills[data-diagram="strategy-governance"] {--skills-wide: 1760 / 1550;}
.archify-panel--story.archify-panel--skills > .story-desktop {height: calc(100vw * .56 + 72px);} .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 / 2600;}
@media (max-width: 700px) { .archify-panel--skills[data-diagram="strategy-baseline"] {--skills-wide: 2400 / 1280; --skills-narrow: 880 / 2200;}
.archify-panel--story.archify-panel--skills > .story-mobile {aspect-ratio: auto; height: 1680px; max-width: 400px; margin-inline: auto;} .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 {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-toolbar {position: sticky; top: 0; z-index: 1; flex-shrink: 0; background: var(--surface);}
.archify-panel--skills .archify-modal iframe {flex: none; height: 1900px; min-height: 1900px;} .archify-panel--skills .archify-modal iframe {flex: none; height: auto; min-height: 0; aspect-ratio: var(--skills-narrow); max-width: 600px; margin-inline: auto;}
} }
@@ -0,0 +1,6 @@
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><title>Measurement has moved</title><meta name="robots" content="noindex"><link rel="canonical" href="https://pages.apps.lego-cloud.eu/eldorado-v1/documentation/strategy/measurement/"><script>
const destinations = {"validated-task-success": "/strategy/measurement/validated-task-success/", "skill-selection-precision": "/strategy/measurement/skill-selection-precision/", "skill-selection-recall": "/strategy/measurement/skill-selection-recall/", "supported-bundle-coverage": "/strategy/measurement/supported-bundle-coverage/", "approved-workflow-adoption": "/strategy/measurement/approved-workflow-adoption/", "change-lead-time": "/strategy/measurement/change-lead-time/", "deployment-frequency": "/strategy/measurement/deployment-frequency/", "change-fail-rate": "/strategy/measurement/change-fail-rate/", "failed-deployment-recovery-time": "/strategy/measurement/failed-deployment-recovery-time/", "deployment-rework-rate": "/strategy/measurement/deployment-rework-rate/", "changes-with-escaped-quality-issues": "/strategy/measurement/changes-with-escaped-quality-issues/", "confirmed-policy-effects": "/strategy/measurement/confirmed-policy-effects/", "cost-per-accepted-outcome": "/strategy/measurement/cost-per-accepted-outcome/", "verification-confidence-and-cognitive-load": "/strategy/measurement/verification-confidence-and-cognitive-load/", "skills-within-substantive-review-window": "/strategy/measurement/skills-within-substantive-review-window/", "workflow-specific-business-acceptance": "/strategy/measurement/workflow-specific-business-acceptance/", "collection-and-safeguards": "/strategy/measurement/collection-and-safeguards/", "decide-what-counts-before-counting": "/strategy/measurement/collection-and-safeguards/#decide-what-counts-before-counting", "baseline-and-interpretation": "/strategy/measurement/collection-and-safeguards/#baseline-and-interpretation", "event-and-privacy-contract": "/strategy/measurement/collection-and-safeguards/#event-and-privacy-contract", "run-the-synthetic-measurement-example": "/strategy/measurement/synthetic-example/", "metric-contracts": "/strategy/measurement/#metric-definitions"};
const anchor = window.location.hash.slice(1);
window.location.replace("/eldorado-v1/documentation" + (Object.hasOwn(destinations, anchor) ? destinations[anchor] : "/strategy/measurement/"));
</script><noscript><meta http-equiv="refresh" content="0;url=/eldorado-v1/documentation/strategy/measurement/"></noscript></head><body><a href="/eldorado-v1/documentation/strategy/measurement/">Continue to Skills Measurement</a></body></html>