Files
workbuddy_skills/product-planning/assets/diagram-design/assets/example-marimekko-full.html
T
admin 8160f91559 技能库同步:另一台机的 2026-10-16 包 + element-picker 入包 + 工具归拢到技能级 assets/
product-planning(同步 26 改 65 新)
- 目录约定:docs/pm/<项目>/ ⇒ <工作区>/项目/<产品slug>/ 为产品根,下分 产品规划/ 与 产品原型/{designs,pages}
- ④段骨架六节 ⇒ 七节(新增「原型地址」;后两节改「页面导航」「原型状态取值」)
- ③段新增真机组件库;结案「源站只有暗色主题」,tokens.css(亮色)标为不可用于生产
- ③段改版规则反转为「一个产品只用一套原型、只留当前态」;②段新增 产品规划模板.md(表头唯一定义处)
- scripts:产品根定位重写(含 .registry/current-project 指针),新增 check_terms.py 与 selftest_product_root.py

session-mechanism(同步 3 改 2 新)
- references/06-技能优化规程.md 新增 §5「要求落在生成时看得见的面上」,总表 16 ⇒ 17 条;SKILL.md 同步
- scripts/collabctl.py 取回 _find_board() 候选定位(旧版写死 scripts/board.py,而看板已搬进 boards/会话看板/ ⇒ 旧版本就是坏的)

element-picker(新装)
- 按用户口径作「独立功能加入 product-planning」,非独立全局技能
- 修掉它自身三处路径漂移(技能根写法 ⇒ 同目录;README 两处旧落点;剪贴板示例文案与代码对齐)

工具归拢到技能级 assets/
- diagram-design / design-capture / element-picker / video-capture 从各段 assets/ 移到技能包 assets/
- 新增红线:本包有两处 assets/(技能级工具箱 vs 段内资产),引工具一律写「技能级 assets/<工具名>/」
- 路径口径由两类改三类(技能级工具箱 / 本段自带资产 / 同根技能)

不入库(磁盘保留)
- design-system-tiaoyue 整个目录(用户 2026-10-16 令):git rm -r --cached + .gitignore,理由=14 MB 且可 CDP 重采;
  已在 ③段 SKILL.md 注明「取仓后这一层是空的属正常,用自带套需另取或重采」
- element-picker 的运行期存档 marks/(服务自己会建目录)
2026-10-16 23:13:33 +08:00

173 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CI minutes by pipeline and runner · Marimekko</title>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root { --color-paper:#f5f5f5; --color-paper-2:#ececec; --color-ink:#2d3142; --color-muted:#4f5d75; --color-soft:#7a8399; --color-rule:rgba(45,49,66,0.12); --color-accent:#eb6c36; --font-sans:'Geist',system-ui,sans-serif; --font-serif:'Instrument Serif',serif; --font-mono:'Geist Mono',ui-monospace,monospace; }
body { font-family: var(--font-sans); background: var(--color-paper); min-height: 100vh; padding: 3rem 2rem; color: var(--color-ink); }
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2.5rem; }
.header-eyebrow { font-family: var(--font-mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.75rem; }
h1 { font-family: var(--font-serif); font-size: clamp(1.75rem, 3vw + 1rem, 2.5rem); font-weight: 400; letter-spacing: -0.02em; line-height: 1.1; margin-bottom: 0.5rem; }
.subtitle { font-size: 1rem; line-height: 1.55; color: var(--color-muted); max-width: 58ch; }
.diagram-container { background: var(--color-paper-2); border-radius: 8px; border: 1px solid var(--color-rule); padding: 1.5rem; overflow-x: auto; }
svg { width: 100%; min-width: 760px; display: block; }
.cards { display: grid; grid-template-columns: 1.1fr 1fr 0.9fr; gap: 1rem; margin-top: 1.5rem; }
@media (max-width: 720px) { .cards { grid-template-columns: 1fr; } }
.card { background: #fff; border-radius: 6px; border: 1px solid var(--color-rule); padding: 1.25rem; }
.card .eyebrow { font-family: var(--font-mono); font-size: 0.5rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-muted); margin-bottom: 0.5rem; }
.card-header { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.875rem; padding-bottom: 0.875rem; border-bottom: 1px solid rgba(45,49,66,0.08); }
.card-dot { width: 7px; height: 7px; border-radius: 50%; }
.card-dot.coral { background: var(--color-accent); } .card-dot.ink { background: var(--color-ink); } .card-dot.muted { background: var(--color-muted); }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card p { color: var(--color-muted); font-size: 0.8125rem; line-height: 1.55; }
.footer { margin-top: 2rem; padding-top: 1.5rem; border-top: 1px solid rgba(45,49,66,0.10); font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; color: var(--color-soft); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<p class="header-eyebrow">Marimekko · Diagram Design</p>
<h1>One pipeline on one runner is a fifth of all CI time</h1>
<p class="subtitle">10,000 CI minutes over 30 days, five pipelines wide and up to three runner operating systems tall. The mobile pipeline is a quarter of the bill, and 83% of it runs on macOS — one segment that is a fifth of everything.</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 500" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="marimekko-full-title marimekko-full-desc">
<title id="marimekko-full-title">One pipeline on one runner is a fifth of all CI time</title>
<desc id="marimekko-full-desc">Marimekko chart of 10,000 CI minutes over 30 days, split five pipelines wide and up to three runner operating systems tall; column width is each pipeline's share of all minutes, segment height is each runner's share within its pipeline, and the mobile pipeline's macOS runners alone account for about 20 percent of the total.</desc>
<defs>
<pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
<circle cx="1" cy="1" r="0.9" fill="rgba(45,49,66,0.10)"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="#f5f5f5"/>
<rect width="100%" height="100%" fill="url(#dots)" opacity="0.55"/>
<!-- ─── COLUMNS ─── Five pipelines across 900px of column width (x 40 → 956
less four 4px gutters), each width the pipeline's share of all 10,000
minutes. Every column is the full 380px plot height (y 40 → 420) and its
segments tile it top to bottom in ONE fixed order - Linux, macOS, Windows -
each height the runner's share of that pipeline. Area is therefore the
joint share, and scripts/verify-marimekko.py recomputes width, height and
area from the data-amount every segment declares. Column edges sit on the
4px grid; segment boundaries are data and land on whole pixels. Non-focal
fills run one ink ramp per series, strongest on the largest series; the
focal segment is marked by its accent stroke, not by tone. ─── -->
<!-- web · 3,140 min · 31.4% · x 40 w 280 -->
<rect x="40" y="40" width="280" height="259" rx="2" fill="#f5f5f5"/>
<rect x="40" y="40" width="280" height="259" rx="2" data-column="web" data-segment="Linux" data-amount="2140" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="web" data-segment="Linux" data-role="label" x="56" y="68" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Linux</text>
<text data-column="web" data-segment="Linux" data-role="label" x="56" y="86" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">2,140 min · 68%</text>
<rect x="40" y="299" width="280" height="46" rx="2" fill="#f5f5f5"/>
<rect x="40" y="299" width="280" height="46" rx="2" data-column="web" data-segment="macOS" data-amount="380" fill="rgba(45,49,66,0.10)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="web" data-segment="macOS" data-role="label" x="56" y="316" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif">macOS</text>
<text data-column="web" data-segment="macOS" data-role="label" x="56" y="331" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">380 min · 12%</text>
<rect x="40" y="345" width="280" height="75" rx="2" fill="#f5f5f5"/>
<rect x="40" y="345" width="280" height="75" rx="2" data-column="web" data-segment="Windows" data-amount="620" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="web" data-segment="Windows" data-role="label" x="56" y="373" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Windows</text>
<text data-column="web" data-segment="Windows" data-role="label" x="56" y="391" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">620 min · 20%</text>
<!-- api · 2,800 min · 28.0% · x 324 w 252 · no macOS runners, so no macOS
segment: a series absent from a column is omitted, never drawn at zero -->
<rect x="324" y="40" width="252" height="334" rx="2" fill="#f5f5f5"/>
<rect x="324" y="40" width="252" height="334" rx="2" data-column="api" data-segment="Linux" data-amount="2460" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="api" data-segment="Linux" data-role="label" x="340" y="68" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Linux</text>
<text data-column="api" data-segment="Linux" data-role="label" x="340" y="86" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">2,460 min · 88%</text>
<rect x="324" y="374" width="252" height="46" rx="2" fill="#f5f5f5"/>
<rect x="324" y="374" width="252" height="46" rx="2" data-column="api" data-segment="Windows" data-amount="340" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="api" data-segment="Windows" data-role="label" x="340" y="391" fill="#2d3142" font-size="11" font-weight="600" font-family="'Geist', sans-serif">Windows</text>
<text data-column="api" data-segment="Windows" data-role="label" x="340" y="406" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">340 min · 12%</text>
<!-- mobile · 2,400 min · 24.0% · x 580 w 216 -->
<rect x="580" y="40" width="216" height="66" rx="2" fill="#f5f5f5"/>
<rect x="580" y="40" width="216" height="66" rx="2" data-column="mobile" data-segment="Linux" data-amount="420" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="mobile" data-segment="Linux" data-role="label" x="596" y="68" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Linux</text>
<text data-column="mobile" data-segment="Linux" data-role="label" x="596" y="86" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">420 min · 18%</text>
<!-- mobile × macOS FOCAL: the one segment the figure is about -->
<rect x="580" y="106" width="216" height="314" rx="2" fill="#f5f5f5"/>
<rect x="580" y="106" width="216" height="314" rx="2" data-column="mobile" data-segment="macOS" data-amount="1980" fill="rgba(235,108,54,0.16)" stroke="#eb6c36" stroke-width="1.5"/>
<text data-column="mobile" data-segment="macOS" data-role="label" x="596" y="134" fill="#2d3142" font-size="14" font-weight="600" font-family="'Geist', sans-serif">macOS</text>
<text data-column="mobile" data-segment="macOS" data-role="label" x="596" y="152" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">1,980 min · 83% of mobile</text>
<!-- infra · 1,400 min · 14.0% · x 800 w 128 -->
<rect x="800" y="40" width="128" height="304" rx="2" fill="#f5f5f5"/>
<rect x="800" y="40" width="128" height="304" rx="2" data-column="infra" data-segment="Linux" data-amount="1120" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="infra" data-segment="Linux" data-role="label" x="816" y="68" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Linux</text>
<text data-column="infra" data-segment="Linux" data-role="label" x="816" y="86" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">1,120 min · 80%</text>
<rect x="800" y="344" width="128" height="76" rx="2" fill="#f5f5f5"/>
<rect x="800" y="344" width="128" height="76" rx="2" data-column="infra" data-segment="Windows" data-amount="280" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-column="infra" data-segment="Windows" data-role="label" x="816" y="372" fill="#2d3142" font-size="12" font-weight="600" font-family="'Geist', sans-serif">Windows</text>
<text data-column="infra" data-segment="Windows" data-role="label" x="816" y="390" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace">280 min · 20%</text>
<!-- docs · 260 min · 2.6% · x 932 w 24. Too narrow for a label, so it carries
the info mark rather than sideways text and is named in the legend. The
column keeps its honest width: a marimekko shows a whole, and a dropped
pipeline makes every other share a lie. -->
<rect x="932" y="40" width="24" height="380" rx="2" fill="#f5f5f5"/>
<rect x="932" y="40" width="24" height="380" rx="2" data-column="docs" data-segment="Linux" data-amount="260" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<circle cx="944" cy="64" r="5" fill="#2d3142"/>
<text x="944" y="67" fill="#f5f5f5" font-size="7" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="middle">i</text>
<!-- Column captions, centred under each column, each printing the column's
share of the whole so the width can be read as a number -->
<text data-column="web" data-role="caption" x="180" y="438" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="middle">web · 31%</text>
<text data-column="api" data-role="caption" x="450" y="438" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="middle">api · 28%</text>
<text data-column="mobile" data-role="caption" x="688" y="438" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="middle">mobile · 24%</text>
<text data-column="infra" data-role="caption" x="864" y="438" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="middle">infra · 14%</text>
<text data-column="docs" data-role="caption" x="944" y="438" fill="#4f5d75" font-size="9" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="middle">docs · 3%</text>
<!-- Legend -->
<line x1="40" y1="462" x2="960" y2="462" stroke="rgba(45,49,66,0.10)" stroke-width="0.8"/>
<text x="40" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.18em">LEGEND</text>
<text x="960" y="478" fill="#4f5d75" font-size="8" font-family="'Geist Mono', monospace" letter-spacing="0.06em" text-anchor="end">WIDTH = PIPELINE SHARE · HEIGHT = RUNNER SHARE WITHIN PIPELINE · AREA = SHARE OF ALL CI MINUTES · 30 DAYS · 10,000 MIN · ILLUSTRATIVE FIGURES</text>
<rect x="40" y="488" width="16" height="10" rx="2" fill="rgba(235,108,54,0.16)" stroke="#eb6c36" stroke-width="1"/>
<text x="64" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">mobile × macOS · the focal segment</text>
<rect x="248" y="488" width="16" height="10" rx="2" fill="rgba(45,49,66,0.16)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-segment="Linux" data-role="key" x="272" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Linux</text>
<rect x="320" y="488" width="16" height="10" rx="2" fill="rgba(45,49,66,0.10)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-segment="macOS" data-role="key" x="344" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">macOS</text>
<rect x="396" y="488" width="16" height="10" rx="2" fill="rgba(45,49,66,0.05)" stroke="rgba(45,49,66,0.30)" stroke-width="1"/>
<text data-segment="Windows" data-role="key" x="420" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">Windows</text>
<circle cx="500" cy="493" r="5" fill="#2d3142"/>
<text x="500" y="496" fill="#f5f5f5" font-size="7" font-weight="600" font-family="'Geist Mono', monospace" text-anchor="middle">i</text>
<text x="516" y="497" fill="#4f5d75" font-size="8.5" font-family="'Geist', sans-serif">docs · 260 min on Linux, 2.6% — too narrow to label</text>
</svg>
</div>
<div class="cards">
<div class="card">
<div class="card-header"><span class="card-dot coral"></span><h3>One segment carries the story</h3></div>
<p>mobile × macOS is the focal segment because it is the finding, not because it sorts first. Width says mobile is 24% of all minutes; height says 83% of those are macOS; the area is the 20% of the whole those two numbers multiply to.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot ink"></span><h3>How to read it</h3></div>
<p>Read across for the pipeline mix, down for the runner mix within a pipeline, and by area for the joint share. Every column is the full plot height, so a segment's height is always a share of its own column, never of the whole — that is what the area is for.</p>
</div>
<div class="card">
<div class="card-header"><span class="card-dot muted"></span><h3>What the chart doesn't show</h3></div>
<p>Minutes, not cost or count. macOS runner minutes are billed at a multiple of Linux, so the macOS segments understate spend; and a wide column can be a few long jobs or many short ones. The figure shows where time goes, not why.</p>
</div>
</div>
<div class="footer">
<span>ci minutes · pipeline × runner os · 30 days · illustrative</span>
<span>example · diagram design</span>
</div>
</div>
</body>
</html>