open-design/skills/pm-spec/example.html
Zakaria a46764fb1b
Some checks failed
ci / Validate workspace (push) Has been cancelled
landing-page-ci / Validate landing page (push) Has been cancelled
landing-page-deploy / Deploy landing page (push) Has been cancelled
github-metrics / Generate repository metrics SVG (push) Has been cancelled
first-commit
2026-05-04 14:58:14 -04:00

249 lines
14 KiB
HTML
Raw Permalink 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" />
<title>Spec — Two-factor authentication for Northwind</title>
<style>
:root {
--bg: #f5f7fa;
--paper: #ffffff;
--ink: #0e1322;
--muted: #5a647a;
--line: #e2e6ee;
--line-strong: #c8cfdb;
--accent: #4a36e3;
--accent-soft: #ece8ff;
--warn: #b8741a;
--positive: #1f8a5a;
--display: 'Charter', Georgia, serif;
--body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, sans-serif;
--mono: ui-monospace, SFMono-Regular, Menlo, monospace;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--body); font-size: 14.5px; line-height: 1.6; }
.page { max-width: 1080px; margin: 28px auto; padding: 0 32px 64px; }
header.top { display: flex; justify-content: space-between; align-items: center; padding: 16px 0; border-bottom: 1px solid var(--line); margin-bottom: 28px; }
.top-left { display: flex; align-items: center; gap: 14px; }
.crumb { font-family: var(--mono); font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; }
.pill.draft { background: var(--accent-soft); color: var(--accent); }
.pill.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.top-actions { display: flex; gap: 8px; font-size: 12.5px; color: var(--muted); }
.top-actions span { padding: 4px 10px; border: 1px solid var(--line); border-radius: 8px; }
h1 { font-family: var(--display); font-size: 42px; line-height: 1.06; letter-spacing: -0.015em; margin: 8px 0 8px; max-width: 22ch; font-weight: 700; }
.summary { font-size: 17px; color: var(--muted); max-width: 64ch; margin: 0 0 28px; }
.meta-row { display: flex; gap: 32px; margin: 14px 0 36px; padding: 16px 22px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; font-size: 13px; }
.meta-row span strong { display: block; font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-bottom: 4px; font-weight: 500; }
section { margin-top: 40px; }
h2 { font-family: var(--display); font-size: 24px; margin: 0 0 4px; letter-spacing: -0.005em; }
h2 small { display: block; font-family: var(--body); font-size: 13px; color: var(--muted); font-weight: 400; margin-top: 4px; line-height: 1.5; letter-spacing: 0; }
/* Problem */
.problem { display: grid; grid-template-columns: 1.5fr 1fr; gap: 14px; margin-top: 14px; }
.panel { padding: 22px 24px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; }
.quote { padding: 22px 24px; background: var(--accent-soft); border-left: 3px solid var(--accent); border-radius: 6px; }
.quote .body { font-family: var(--display); font-size: 17px; line-height: 1.5; }
.quote .author { font-family: var(--mono); font-size: 11.5px; color: var(--muted); margin-top: 12px; text-transform: uppercase; letter-spacing: 0.06em; }
/* Goals */
.goals { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.goal-list { padding: 22px 24px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; }
.goal-list h3 { font-family: var(--display); font-size: 16px; margin: 0 0 10px; }
.goal-list h3 .tick { display: inline-flex; width: 18px; height: 18px; border-radius: 50%; align-items: center; justify-content: center; margin-right: 8px; font-size: 11px; }
.goal-list h3 .tick.yes { background: var(--positive); color: white; }
.goal-list h3 .tick.no { background: var(--line-strong); color: var(--muted); }
.goal-list ul { padding-left: 18px; margin: 0; display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
/* Metrics table */
table { width: 100%; border-collapse: collapse; margin-top: 14px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
th, td { padding: 12px 18px; text-align: left; font-size: 13.5px; border-bottom: 1px solid var(--line); }
th { font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); background: #f8fafd; }
tr:last-child td { border-bottom: none; }
td.target { font-family: var(--mono); color: var(--accent); font-weight: 600; }
/* Stories */
.stories { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.story { padding: 18px 22px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; display: grid; grid-template-columns: auto 1fr; gap: 16px; align-items: center; }
.story-num { width: 30px; height: 30px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); display: inline-flex; align-items: center; justify-content: center; font-family: var(--mono); font-weight: 600; font-size: 13px; }
.story-text { font-size: 14.5px; }
.story-text strong { color: var(--accent); }
/* Milestones */
.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 14px; }
.step { padding: 18px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; position: relative; }
.step .badge { display: inline-block; padding: 3px 8px; border-radius: 999px; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 8px; background: var(--accent-soft); color: var(--accent); }
.step h4 { font-family: var(--display); font-size: 15px; margin: 0 0 6px; }
.step .meta { font-family: var(--mono); font-size: 11px; color: var(--muted); margin-bottom: 8px; }
.step ul { padding-left: 16px; margin: 0; font-size: 13px; display: flex; flex-direction: column; gap: 4px; }
/* Open questions */
.questions { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.question { padding: 16px 20px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: center; }
.question p { margin: 0; font-size: 14px; }
.assignee { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.avatar { width: 22px; height: 22px; border-radius: 50%; background: linear-gradient(135deg, var(--accent), #8473ff); color: white; font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
footer { margin-top: 60px; padding-top: 18px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; font-family: var(--mono); font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
@media (max-width: 880px) {
.problem, .goals { grid-template-columns: 1fr; }
.timeline { grid-template-columns: 1fr 1fr; }
h1 { font-size: 32px; }
}
</style>
</head>
<body>
<div class="page">
<header class="top">
<div class="top-left">
<span class="crumb">Northwind / Specs / Auth</span>
<span class="pill draft"><span class="pill dot"></span>Draft v0.4</span>
</div>
<div class="top-actions">
<span>Owner · Devon Park</span>
<span>Updated · 22 Oct 2025</span>
<span>Reviewers · 4</span>
</div>
</header>
<h1>Two-factor authentication for the Northwind app.</h1>
<p class="summary">Add TOTP and security-key second factors to the Northwind login flow so enterprise customers can meet their internal controls and we can move from "considered" to "approved" on three pending deals.</p>
<div class="meta-row">
<span><strong>Squad</strong>Identity Platform</span>
<span><strong>Engineering lead</strong>Priya Banerjee</span>
<span><strong>Design lead</strong>Sasha Lin</span>
<span><strong>Target launch</strong>End Q4 (Dec 18)</span>
<span><strong>Effort</strong>~6 eng-weeks</span>
</div>
<section>
<h2>Problem<small>What hurts today, and for whom.</small></h2>
<div class="problem">
<div class="panel">
<p>Three of the last six enterprise security reviews flagged the absence of a second factor as a blocker. Today, password is the only thing standing between a phished credential and a workspace full of customer data — for tenants under SOC 2 Type II expectations that's not just a perception problem, it's a control-plane gap.</p>
<p>It also affects internal staff: every engineer with prod access is on the same auth surface as a marketing-team viewer. We rely on policy, not posture.</p>
</div>
<div class="quote">
<div class="body">"We love the product, but the absence of TOTP came up in two of three security reviews. Add it and we can sign."</div>
<div class="author">— Maya Reddy · CTO, Pioneer Robotics</div>
</div>
</div>
</section>
<section>
<h2>Goals &amp; non-goals<small>What this spec ships, and what we're explicitly leaving for later.</small></h2>
<div class="goals">
<div class="goal-list">
<h3><span class="tick yes"></span>Goals</h3>
<ul>
<li>TOTP support (Authy, 1Password, Google Authenticator) for all paid plans.</li>
<li>Security key support (WebAuthn) for Enterprise plans.</li>
<li>Workspace-level enforcement: admin can require 2FA for all members.</li>
<li>Recovery codes — printable, downloadable, regeneratable.</li>
<li>Audit log entries for setup, change, and removal events.</li>
</ul>
</div>
<div class="goal-list">
<h3><span class="tick no">×</span>Non-goals</h3>
<ul>
<li>SMS as a second factor (NIST deprecated; not adding).</li>
<li>SSO replacement — SAML stays a separate workstream.</li>
<li>Per-action step-up (future spec, owned by Identity).</li>
<li>Custom 2FA brand voice for whitelabel deployments.</li>
</ul>
</div>
</div>
</section>
<section>
<h2>Success metrics<small>We'll judge this launch on the three numbers below at the 30 / 60 / 90 day marks.</small></h2>
<table>
<thead><tr><th>Metric</th><th>Baseline</th><th>Target (90d)</th><th>How we measure</th></tr></thead>
<tbody>
<tr><td>Enterprise deals unblocked by 2FA gap</td><td>0 of 3</td><td class="target">3 of 3</td><td>Sales motion notes + signed contract count</td></tr>
<tr><td>Member 2FA adoption (paid workspaces)</td><td>n/a</td><td class="target">≥ 60%</td><td>auth.factor_enrolled events / DAU</td></tr>
<tr><td>Account takeover incidents (rolling 30d)</td><td>4 last quarter</td><td class="target">≤ 1</td><td>Security incident tracker (SEV-3+)</td></tr>
<tr><td>Support load from 2FA recovery</td><td>n/a</td><td class="target">&lt; 1.5% of tickets</td><td>Tagged "auth-2fa" in Zendesk</td></tr>
</tbody>
</table>
</section>
<section>
<h2>User stories<small>Three personas, three motions.</small></h2>
<div class="stories">
<div class="story">
<div class="story-num">1</div>
<div class="story-text">As a <strong>workspace admin</strong>, I want to require 2FA for everyone in my workspace, so that I can pass our annual SOC 2 control review.</div>
</div>
<div class="story">
<div class="story-num">2</div>
<div class="story-text">As a <strong>day-to-day member</strong>, I want to enroll a TOTP app in under two minutes, so that I'm not pulled out of work to reconfigure auth.</div>
</div>
<div class="story">
<div class="story-num">3</div>
<div class="story-text">As a <strong>support engineer</strong>, I want a clear path to help locked-out users without bypassing their second factor, so that we don't undo the security we just added.</div>
</div>
</div>
</section>
<section>
<h2>Rollout milestones<small>Four phases. Each phase ships behind a flag.</small></h2>
<div class="timeline">
<div class="step">
<span class="badge">M1 · Nov 4</span>
<h4>TOTP enrollment</h4>
<div class="meta">2 eng-weeks</div>
<ul><li>Settings page UI</li><li>Recovery codes</li><li>Audit log entries</li></ul>
</div>
<div class="step">
<span class="badge">M2 · Nov 18</span>
<h4>Login flow</h4>
<div class="meta">1.5 eng-weeks</div>
<ul><li>Challenge step in login</li><li>Trusted-device cookie</li><li>Rate limiting</li></ul>
</div>
<div class="step">
<span class="badge">M3 · Dec 2</span>
<h4>WebAuthn + admin enforcement</h4>
<div class="meta">2 eng-weeks</div>
<ul><li>Security keys (Enterprise)</li><li>Workspace policy</li><li>Member nag prompt</li></ul>
</div>
<div class="step">
<span class="badge">M4 · Dec 18</span>
<h4>GA + comms</h4>
<div class="meta">0.5 eng-weeks</div>
<ul><li>Changelog + email</li><li>Help center articles</li><li>Sales enablement</li></ul>
</div>
</div>
</section>
<section>
<h2>Open questions<small>Assigned. We need answers by Friday Oct 31 to keep the date.</small></h2>
<div class="questions">
<div class="question">
<p>Should we let members choose between TOTP and security keys, or pick the strongest available factor for them?</p>
<span class="assignee"><span class="avatar">DP</span>Devon Park · Oct 28</span>
</div>
<div class="question">
<p>Trusted-device cookie lifetime: 7 days, 30 days, or admin-configurable?</p>
<span class="assignee"><span class="avatar">PB</span>Priya Banerjee · Oct 29</span>
</div>
<div class="question">
<p>Do we surface a member's 2FA status in the admin user list, or only in the audit log?</p>
<span class="assignee"><span class="avatar">SL</span>Sasha Lin · Oct 30</span>
</div>
</div>
</section>
<footer>
<span>Northwind Identity Platform · spec-2fa</span>
<span>v0.4 · 22 October 2025</span>
</footer>
</div>
</body>
</html>