Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf46363ea9 | |||
| 6974800d2e | |||
| a97c2a4034 | |||
| a1eb1e79ea | |||
| c2b35d5355 | |||
| aefbd206f9 | |||
| 0bca43cbfd | |||
| 47fff7d289 | |||
| 2b621843a2 | |||
| 9ca3f48def | |||
| 12bc1dc067 | |||
| 71c74fb7f1 | |||
| edd03f9716 | |||
| 6bfae2033e | |||
| af1133f019 | |||
| c2a046d6f7 | |||
| 64a7f2e4c5 | |||
| 83c956c2b6 | |||
| 208ec9447b |
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env node
|
||||
// Compare the *rendered* text of a legacy page against its Astro replacement.
|
||||
//
|
||||
// node .agents/scripts/rendered-text-diff.mjs full-guide
|
||||
// node .agents/scripts/rendered-text-diff.mjs full-guide --pt
|
||||
//
|
||||
// Why this exists: scripts/verify.mjs reads the legacy files, so a migrated
|
||||
// page can drop half its content and still pass the gate. Task 15d shipped
|
||||
// /full-guide/ missing 86 rendered spans -- the entire verification section,
|
||||
// the hands-on exercise brief, and both "Clone from Gitea" links -- and every
|
||||
// check was green.
|
||||
//
|
||||
// Static HTML comparison is useless here: the guide's tab panels are injected
|
||||
// by an island at runtime, so half the legacy page's markup has no static
|
||||
// counterpart. This walks the live DOM instead and skips anything the browser
|
||||
// is not painting -- which also drops the hidden Portuguese half of each
|
||||
// bilingual pair, so the two sides line up.
|
||||
//
|
||||
// Requires playwright (devDependency) and two static servers; it starts both.
|
||||
import { spawn } from 'node:child_process';
|
||||
import { cpSync, mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
// `index` is the landing page: it lives at the repository root, not in a
|
||||
// directory of its own, so it needs a different path on the legacy side.
|
||||
const route = process.argv[2];
|
||||
if (!route) {
|
||||
console.error('usage: rendered-text-diff.mjs <route> [--pt] e.g. full-guide, or index');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
// `--pt` clicks the language toggle on both pages first. English parity is
|
||||
// only half the contract: a page can render every English string and still
|
||||
// leave a restored block untranslated, because the Portuguese half is a
|
||||
// separate set of nodes. Only /full-guide/ and /rules/ have a toggle.
|
||||
const portuguese = process.argv.includes('--pt');
|
||||
const legacyPath = route === 'index' ? 'index.html' : `${route}/index.html`;
|
||||
const astroPath = route === 'index' ? '' : `${route}/`;
|
||||
|
||||
// The built site expects to be served under the configured base path.
|
||||
const staging = mkdtempSync(join(tmpdir(), 'af-rtd-'));
|
||||
cpSync('dist', join(staging, 'ai-for-dummies'), { recursive: true });
|
||||
|
||||
const serve = (dir, port) =>
|
||||
spawn('python3', ['-m', 'http.server', String(port), '-d', dir], { stdio: 'ignore' });
|
||||
const servers = [serve('.', 4197), serve(staging, 4196)];
|
||||
const stop = () => {
|
||||
servers.forEach((s) => s.kill());
|
||||
rmSync(staging, { recursive: true, force: true });
|
||||
};
|
||||
|
||||
// Visible text nodes, in document order, whitespace collapsed.
|
||||
const visibleText = () => {
|
||||
const out = [];
|
||||
const walk = (node) => {
|
||||
for (const child of node.childNodes) {
|
||||
if (child.nodeType === Node.TEXT_NODE) {
|
||||
const text = child.textContent.replace(/\s+/g, ' ').trim();
|
||||
if (text) out.push(text);
|
||||
continue;
|
||||
}
|
||||
if (child.nodeType !== Node.ELEMENT_NODE) continue;
|
||||
if (child.tagName === 'SCRIPT' || child.tagName === 'STYLE') continue;
|
||||
const style = getComputedStyle(child);
|
||||
if (child.hidden || style.display === 'none' || style.visibility === 'hidden') continue;
|
||||
walk(child);
|
||||
}
|
||||
};
|
||||
walk(document.body);
|
||||
return out;
|
||||
};
|
||||
|
||||
try {
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
const browser = await chromium.launch();
|
||||
const grab = async (url) => {
|
||||
const page = await browser.newPage({ viewport: { width: 1400, height: 1000 } });
|
||||
await page.goto(url, { waitUntil: 'load' });
|
||||
// The islands hydrate and render their initial panel on load; without this
|
||||
// every panel's copy reads as missing.
|
||||
await page.waitForTimeout(1200);
|
||||
if (portuguese) {
|
||||
const toggle = await page.$('[data-lang="pt"]');
|
||||
if (!toggle) throw new Error(`no language toggle on ${url}`);
|
||||
await toggle.click();
|
||||
await page.waitForTimeout(1200);
|
||||
}
|
||||
const spans = await page.evaluate(visibleText);
|
||||
await page.close();
|
||||
return spans;
|
||||
};
|
||||
|
||||
const legacy = await grab(`http://localhost:4197/${legacyPath}`);
|
||||
const astro = await grab(`http://localhost:4196/ai-for-dummies/${astroPath}`);
|
||||
await browser.close();
|
||||
|
||||
const rendered = new Set(astro);
|
||||
const missing = legacy.filter((span) => !rendered.has(span));
|
||||
|
||||
// Both directions. A string the Astro page paints and the legacy page does
|
||||
// not is just as wrong: it means a translation was invented, or an English
|
||||
// string was left standing where the legacy page swaps it.
|
||||
const legacySpans = new Set(legacy);
|
||||
const extra = astro.filter((span) => !legacySpans.has(span));
|
||||
|
||||
const mode = portuguese ? 'pt' : 'en';
|
||||
console.log(
|
||||
`${mode} · legacy ${legacy.length} spans · astro ${astro.length} spans · missing ${missing.length} · extra ${extra.length}`,
|
||||
);
|
||||
for (const span of missing) console.log(` - ${span}`);
|
||||
for (const span of extra) console.log(` + ${span}`);
|
||||
process.exitCode = missing.length === 0 && extra.length === 0 ? 0 : 1;
|
||||
} finally {
|
||||
stop();
|
||||
}
|
||||
+1
-88
@@ -1,88 +1 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>AI For Dummies — Agents and trees</title>
|
||||
<link rel="stylesheet" href="../chapters.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header class="top">
|
||||
<a href="../summary/">← ROUTE MAP</a><span>02 / AGENTS & TREES</span
|
||||
><a href="../full-guide/">field guide ↗</a>
|
||||
</header>
|
||||
<section class="hero">
|
||||
<p class="eyebrow">Subagent workflow</p>
|
||||
<h1>One branch<br />per <em>hand.</em></h1>
|
||||
<p>
|
||||
Agents work when roles, files, and evidence are bounded. A worktree gives each worker its
|
||||
own checkout while the orchestrator protects intent.
|
||||
</p>
|
||||
</section>
|
||||
<section class="pipeline">
|
||||
<div>
|
||||
<p class="eyebrow">The tree</p>
|
||||
<h2>Split at<br />the <em>seam.</em></h2>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<strong>MAIN / ORCHESTRATOR</strong
|
||||
><code
|
||||
>├── agent/ui → components + visual states · ├── agent/tests → acceptance + regressions
|
||||
· └── agent/docs → guide + examples · merge after each leaf returns a diff and
|
||||
evidence</code
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
<section class="grid">
|
||||
<article class="card">
|
||||
<b>FRAME</b>
|
||||
<h2>Orchestrator</h2>
|
||||
<p>Owns scope, task graph, boundaries, and integration.</p>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>HAND OFF</b>
|
||||
<h2>Worker</h2>
|
||||
<p>Owns one coherent slice and one worktree.</p>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>PROVE</b>
|
||||
<h2>Verifier</h2>
|
||||
<p>Re-runs gates and reports remaining gaps.</p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="practice">
|
||||
<div>
|
||||
<p class="eyebrow">Handoff</p>
|
||||
<h2>Context that<br />can <em>travel.</em></h2>
|
||||
</div>
|
||||
<div class="steps">
|
||||
<article>
|
||||
<b>01</b>
|
||||
<div>
|
||||
<strong>Brief</strong
|
||||
><span>Goal, owned files, dependencies, non-goals, acceptance.</span>
|
||||
</div>
|
||||
</article>
|
||||
<article>
|
||||
<b>02</b>
|
||||
<div>
|
||||
<strong>Isolation</strong><span>One branch and worktree per independent change.</span>
|
||||
</div>
|
||||
</article>
|
||||
<article>
|
||||
<b>03</b>
|
||||
<div>
|
||||
<strong>Evidence</strong
|
||||
><span>Commands, result, changed files, screenshots, gaps.</span>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<nav class="links">
|
||||
<a href="../models/">Previous: models →</a><a href="../rules/">Rules case study →</a
|
||||
><a href="../hands-on/rules/">Try the rules lab →</a>
|
||||
</nav>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>AI For Dummies — Agents and trees</title><link rel="stylesheet" href="../chapters.css"></head><body><main><header class="top"><a href="../summary/">← ROUTE MAP</a><span>02 / AGENTS & TREES</span><a href="../full-guide/">field guide ↗</a></header><section class="hero"><p class="eyebrow">Subagent workflow</p><h1>One branch<br>per <em>hand.</em></h1><p>Agents work when roles, files, and evidence are bounded. A worktree gives each worker its own checkout while the orchestrator protects intent.</p></section><section class="pipeline"><div><p class="eyebrow">The tree</p><h2>Split at<br>the <em>seam.</em></h2></div><div class="panel"><strong>MAIN / ORCHESTRATOR</strong><code>├── agent/ui → components + visual states · ├── agent/tests → acceptance + regressions · └── agent/docs → guide + examples · merge after each leaf returns a diff and evidence</code></div></section><section class="grid"><article class="card"><b>FRAME</b><h2>Orchestrator</h2><p>Owns scope, task graph, boundaries, and integration.</p></article><article class="card"><b>HAND OFF</b><h2>Worker</h2><p>Owns one coherent slice and one worktree.</p></article><article class="card"><b>PROVE</b><h2>Verifier</h2><p>Re-runs gates and reports remaining gaps.</p></article></section><section class="practice"><div><p class="eyebrow">Handoff</p><h2>Context that<br>can <em>travel.</em></h2></div><div class="steps"><article><b>01</b><div><strong>Brief</strong><span>Goal, owned files, dependencies, non-goals, acceptance.</span></div></article><article><b>02</b><div><strong>Isolation</strong><span>One branch and worktree per independent change.</span></div></article><article><b>03</b><div><strong>Evidence</strong><span>Commands, result, changed files, screenshots, gaps.</span></div></article></div></section><nav class="links"><a href="../models/">Previous: models →</a><a href="../rules/">Rules case study →</a><a href="../hands-on/rules/">Try the rules lab →</a></nav></main></body></html>
|
||||
|
||||
+19
-71
@@ -1,79 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="AI For Dummies: a practical route map for models, agents, worktrees, skills, rules, and verification."
|
||||
/>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="description" content="AI For Dummies: a practical route map for models, agents, worktrees, skills, rules, and verification.">
|
||||
<title>AI For Dummies — Start here</title>
|
||||
<link rel="stylesheet" href="chapters.css" />
|
||||
<link rel="stylesheet" href="landing.css" />
|
||||
</head>
|
||||
<body>
|
||||
<link rel="stylesheet" href="chapters.css">
|
||||
<link rel="stylesheet" href="landing.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header class="top">
|
||||
<a href="./" aria-current="page">AI FOR DUMMIES</a><span>00 / START HERE</span
|
||||
><a href="skills-review/">review desk ↗</a>
|
||||
</header>
|
||||
<section class="hero">
|
||||
<p class="eyebrow">The short route</p>
|
||||
<h1>Ship the<br /><em>system.</em></h1>
|
||||
<p>
|
||||
Start with the map. Then open the one chapter that matches the decision in front of you:
|
||||
model, agent, worktree, skill, rule, or proof.
|
||||
</p>
|
||||
<a class="guide-launch" href="full-guide/">Take the full field guide <span>→</span></a>
|
||||
</section>
|
||||
<header class="top"><a href="./" aria-current="page">AI FOR DUMMIES</a><span>00 / START HERE</span><a href="skills-review/">review desk ↗</a></header>
|
||||
<section class="hero"><p class="eyebrow">The short route</p><h1>Ship the<br><em>system.</em></h1><p>Start with the map. Then open the one chapter that matches the decision in front of you: model, agent, worktree, skill, rule, or proof.</p><a class="guide-launch" href="full-guide/">Take the full field guide <span>→</span></a></section>
|
||||
<section class="grid route-grid" aria-label="Guide chapters">
|
||||
<article class="card">
|
||||
<b>01</b>
|
||||
<h2>Models</h2>
|
||||
<p>Capability and effort are separate knobs.</p>
|
||||
<a href="models/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>02</b>
|
||||
<h2>Agents & trees</h2>
|
||||
<p>Bound roles, handoffs, and worktrees.</p>
|
||||
<a href="agents/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>03</b>
|
||||
<h2>Skills</h2>
|
||||
<p>Capture repeatable decisions in small packages.</p>
|
||||
<a href="skills/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>04</b>
|
||||
<h2>Rules</h2>
|
||||
<p>Connect guidance to enforcement.</p>
|
||||
<a href="rules/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>05</b>
|
||||
<h2>Hands-on</h2>
|
||||
<p>Compare a strong prompt with skill-enabled work.</p>
|
||||
<a href="hands-on/starter/">Open lab →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>06</b>
|
||||
<h2>Review desk</h2>
|
||||
<p>Browse original packages, references, scripts, and improvements.</p>
|
||||
<a href="skills-review/">Open desk →</a>
|
||||
</article>
|
||||
<article class="card"><b>01</b><h2>Models</h2><p>Capability and effort are separate knobs.</p><a href="models/">Open chapter →</a></article>
|
||||
<article class="card"><b>02</b><h2>Agents & trees</h2><p>Bound roles, handoffs, and worktrees.</p><a href="agents/">Open chapter →</a></article>
|
||||
<article class="card"><b>03</b><h2>Skills</h2><p>Capture repeatable decisions in small packages.</p><a href="skills/">Open chapter →</a></article>
|
||||
<article class="card"><b>04</b><h2>Rules</h2><p>Connect guidance to enforcement.</p><a href="rules/">Open chapter →</a></article>
|
||||
<article class="card"><b>05</b><h2>Hands-on</h2><p>Compare a strong prompt with skill-enabled work.</p><a href="hands-on/starter/">Open lab →</a></article>
|
||||
<article class="card"><b>06</b><h2>Review desk</h2><p>Browse original packages, references, scripts, and improvements.</p><a href="skills-review/">Open desk →</a></article>
|
||||
</section>
|
||||
<section class="landing-note">
|
||||
<span>THE THREAD</span
|
||||
><strong
|
||||
>Frame uncertainty → isolate execution → preserve judgment → verify the change.</strong
|
||||
>
|
||||
</section>
|
||||
<footer>
|
||||
The route map is now the default entry. The full guide remains available whenever you want
|
||||
the whole narrative.
|
||||
</footer>
|
||||
<section class="landing-note"><span>THE THREAD</span><strong>Frame uncertainty → isolate execution → preserve judgment → verify the change.</strong></section>
|
||||
<footer>The route map is now the default entry. The full guide remains available whenever you want the whole narrative.</footer>
|
||||
</main>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+1
-87
@@ -1,87 +1 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>AI For Dummies — Models</title>
|
||||
<link rel="stylesheet" href="../chapters.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header class="top">
|
||||
<a href="../summary/">← ROUTE MAP</a><span>01 / MODELS</span
|
||||
><a href="../full-guide/">field guide ↗</a>
|
||||
</header>
|
||||
<section class="hero">
|
||||
<p class="eyebrow">Model routing</p>
|
||||
<h1>Choose the<br /><em>engine.</em></h1>
|
||||
<p>
|
||||
A model has a capability ceiling. Effort controls how much room it gets to reason. Route
|
||||
by uncertainty and verification cost.
|
||||
</p>
|
||||
</section>
|
||||
<section class="grid">
|
||||
<article class="card">
|
||||
<b>LOW</b>
|
||||
<h2>Bounded rhythm</h2>
|
||||
<p>Lookup, small edits, formatting, and transformations with clear checks.</p>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>MEDIUM</b>
|
||||
<h2>Default work</h2>
|
||||
<p>Normal implementation where the contract is clear but context matters.</p>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>HIGH</b>
|
||||
<h2>Ambiguity</h2>
|
||||
<p>Planning, architecture, security judgment, and hard failures.</p>
|
||||
</article>
|
||||
</section>
|
||||
<section class="model">
|
||||
<div>
|
||||
<p class="eyebrow">Two knobs</p>
|
||||
<h2>Capability<br />× effort</h2>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<strong>ROUTING RULE</strong
|
||||
><code
|
||||
>strong model + high effort → frame ambiguity · light model + low effort → bounded
|
||||
execution · raise one knob at a time → compare evidence</code
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
<section class="practice">
|
||||
<div>
|
||||
<p class="eyebrow">Sequence</p>
|
||||
<h2>Spend judgment<br />where it <em>compounds.</em></h2>
|
||||
</div>
|
||||
<div class="steps">
|
||||
<article>
|
||||
<b>01</b>
|
||||
<div>
|
||||
<strong>Plan</strong
|
||||
><span>Strong model: scope, risks, acceptance, and worktree split.</span>
|
||||
</div>
|
||||
</article>
|
||||
<article>
|
||||
<b>02</b>
|
||||
<div>
|
||||
<strong>Build</strong
|
||||
><span>Focused worker: smallest context and lightest model that can pass.</span>
|
||||
</div>
|
||||
</article>
|
||||
<article>
|
||||
<b>03</b>
|
||||
<div>
|
||||
<strong>Review</strong
|
||||
><span>Independent pass when missed issues cost more than the call.</span>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
<nav class="links">
|
||||
<a href="../agents/">Next: agents & trees →</a><a href="../rules/">Rules case study →</a>
|
||||
</nav>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>AI For Dummies — Models</title><link rel="stylesheet" href="../chapters.css"></head><body><main><header class="top"><a href="../summary/">← ROUTE MAP</a><span>01 / MODELS</span><a href="../full-guide/">field guide ↗</a></header><section class="hero"><p class="eyebrow">Model routing</p><h1>Choose the<br><em>engine.</em></h1><p>A model has a capability ceiling. Effort controls how much room it gets to reason. Route by uncertainty and verification cost.</p></section><section class="grid"><article class="card"><b>LOW</b><h2>Bounded rhythm</h2><p>Lookup, small edits, formatting, and transformations with clear checks.</p></article><article class="card"><b>MEDIUM</b><h2>Default work</h2><p>Normal implementation where the contract is clear but context matters.</p></article><article class="card"><b>HIGH</b><h2>Ambiguity</h2><p>Planning, architecture, security judgment, and hard failures.</p></article></section><section class="model"><div><p class="eyebrow">Two knobs</p><h2>Capability<br>× effort</h2></div><div class="panel"><strong>ROUTING RULE</strong><code>strong model + high effort → frame ambiguity · light model + low effort → bounded execution · raise one knob at a time → compare evidence</code></div></section><section class="practice"><div><p class="eyebrow">Sequence</p><h2>Spend judgment<br>where it <em>compounds.</em></h2></div><div class="steps"><article><b>01</b><div><strong>Plan</strong><span>Strong model: scope, risks, acceptance, and worktree split.</span></div></article><article><b>02</b><div><strong>Build</strong><span>Focused worker: smallest context and lightest model that can pass.</span></div></article><article><b>03</b><div><strong>Review</strong><span>Independent pass when missed issues cost more than the call.</span></div></article></div></section><nav class="links"><a href="../agents/">Next: agents & trees →</a><a href="../rules/">Rules case study →</a></nav></main></body></html>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"eslint-plugin-astro": "^1",
|
||||
"husky": "^9",
|
||||
"lint-staged": "^16",
|
||||
"playwright": "^1.62.1",
|
||||
"prettier": "^3",
|
||||
"prettier-plugin-astro": "^0.14",
|
||||
"stylelint": "^16",
|
||||
|
||||
@@ -40,3 +40,86 @@ together.
|
||||
- [ ] Ported rules live with the component that needs them, or in `tokens.css`
|
||||
- [ ] `responsive.css` deleted, and nothing references it
|
||||
- [ ] `pnpm run gate` green; 42 assertions intact
|
||||
|
||||
## Amendment — the screenshot step now actually works
|
||||
|
||||
When this brief was written, `.agents/scripts/visual-regression.mjs` threw
|
||||
`playwright ... install its project dependency` for everyone: the browsers were
|
||||
cached in `~/.cache/ms-playwright` but the node package was never a dependency.
|
||||
Every "screenshots match" checkbox in this plan was unsatisfiable. `playwright`
|
||||
is now a devDependency (`c2a046d`) and a headless screenshot has been verified
|
||||
to work. **Take the screenshots.** "I read the rules and they look unused" is
|
||||
not the proof this brief asks for.
|
||||
|
||||
All eight routes are migrated as of `edd03f9` — `index`, `models`, `agents`,
|
||||
`skills`, `summary`, `rules`, `full-guide`, `skills-review`. Screenshot all of
|
||||
them, not just `/full-guide/`.
|
||||
|
||||
## Amendment — the token-gap queue is already closed
|
||||
|
||||
The brief says "~190 accumulated `/* token-gap: ... */` markers … do not start".
|
||||
Task 02c ran and took them from 154 to **17**. Do not reopen that work either;
|
||||
the 17 that remain are deliberate.
|
||||
|
||||
Two things 02c got wrong that you must not repeat. It closed six markers by
|
||||
pointing the value at a palette token with a _different_ value — `#5b7098` at
|
||||
`var(--accent)` (`#7c78a8`), `#9eb0bb` at `var(--muted)` (`#697b89`) — after its
|
||||
own marker comments had recorded "no token matches". That shipped a silent
|
||||
redesign the gate could not see. `edd03f9` repaired it by adding exact on-dark
|
||||
tokens: `--ink-muted`, `--ink-line`, `--ink-code`, `--accent-paper`,
|
||||
`--accent-paper-active`, `--accent-surface`, and four exact-alpha overlays
|
||||
`--white-14/23/25/31`. Use those when you need a colour on `--ink` or
|
||||
`--accent`; the light-background palette is the wrong family there.
|
||||
|
||||
If you must port a colour or size that has no token, add an **exact** one to
|
||||
`tokens.css` — you own it — or leave the raw value under a `token-gap:` marker.
|
||||
Never round to a near neighbour.
|
||||
|
||||
## Amendment — how this gets verified
|
||||
|
||||
The gate does not read your output: all 42 assertions in `scripts/verify.mjs`
|
||||
read the legacy files, so deleting `responsive.css` and breaking every mobile
|
||||
layout passes it cleanly. Before you claim done, diff the **built** CSS
|
||||
(`dist/_astro/*.css`) against `main` and enumerate every declaration that
|
||||
disappeared. Each one is either present elsewhere or a regression.
|
||||
|
||||
## Attempt 1 stopped short — and the goal as written is not reachable yet
|
||||
|
||||
Preserved as `rejected/15e-attempt-1` (`d2312d7`). Start by reading that diff:
|
||||
it ported rules into `ChapterHero`, `FleetDiagram`, `RouteTable`, `WorktreeMap`,
|
||||
`LanguageToggle`, `ReadingProgress`, `agents.astro`, `full-guide.astro` and
|
||||
`tokens.css`, and it added `pixelmatch`, `pngjs` and `postcss` plus a working
|
||||
screenshot-compare harness. Reuse all of it. It never finished the comparison
|
||||
loop and never committed on its own.
|
||||
|
||||
It also deleted `responsive.css` while **`full-guide/index.html` still links
|
||||
it**. That page is the live site until task 20 cuts over; deleting the
|
||||
stylesheet strips it. `src/components/islands/GuideSelector.astro` still
|
||||
references it too.
|
||||
|
||||
So the "delete it" checkbox in this brief cannot be honestly ticked before the
|
||||
cutover. Do not tick it, and do not delete the file. **The deliverable is
|
||||
everything up to the deletion:**
|
||||
|
||||
- Every rule `responsive.css` provides to an Astro route lives with the
|
||||
component that needs it, or in `tokens.css`.
|
||||
- No `.astro` file and no Astro layout links `responsive.css` any more.
|
||||
- `full-guide/index.html` still links it, unchanged, and still renders exactly
|
||||
as it does today.
|
||||
- A list, in this file, of the rules that remain in `responsive.css` solely for
|
||||
the legacy page — that list is the deletion checklist task 20 will execute.
|
||||
|
||||
Amend the "Done when" boxes to match before you start, and say in your final
|
||||
report that the file is intentionally still present.
|
||||
|
||||
### One rule to port properly while you are in there
|
||||
|
||||
`.route-meter span` animates `height: var(--score)` — a layout property, which
|
||||
task 18 was meant to eliminate. 18 changed it in `responsive.css` and that
|
||||
change was reverted (`0bca43c`) because it belongs in the component, not in a
|
||||
legacy file. When you port this rule, port it as
|
||||
`transform: scaleY(var(--score)); transform-origin: bottom` with `height: 100%`.
|
||||
`--score` is set inline as a percentage (`--score:92%`) by `GuideSelector.astro`
|
||||
and `full-guide.astro`; a percentage in `scaleY()` is valid and was verified
|
||||
rendering correctly at `matrix(1, 0, 0, 0.92, 0, 0)`. Leave the legacy
|
||||
`responsive.css` copy of the rule alone.
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
# Task 15f — Restore the content 15d dropped from /full-guide/
|
||||
|
||||
**Agent**: `page-migrator` · **Model**: Codex **Depends on**: 15d **Blocks**:
|
||||
19, 20 **Worktree**: `.agents/scripts/worktree.sh start 15f full-guide-restore`
|
||||
|
||||
## Goal
|
||||
|
||||
`/full-guide/` is missing about a fifth of the page. Put it back, in the right
|
||||
place, bilingual where the legacy page is bilingual.
|
||||
|
||||
Task 15d assembled the page and passed every check. It also dropped 87 rendered
|
||||
text spans. One of them — the `.chapter-route` section, the only route out of
|
||||
the guide to the summary, models, agents and skills chapters — has already been
|
||||
restored (`c2b35d5`). The remaining 86 are your task.
|
||||
|
||||
Nothing caught this. `scripts/verify.mjs` reads `full-guide/index.html`, the
|
||||
legacy file, which still has every one of these sections. The gate was green the
|
||||
whole time. Task 19 is blocked on you: it re-pointed its assertions at `dist/`
|
||||
and they now fail, correctly, on exactly this content.
|
||||
|
||||
## How to see the gap
|
||||
|
||||
```
|
||||
pnpm run build
|
||||
node .agents/scripts/rendered-text-diff.mjs full-guide
|
||||
```
|
||||
|
||||
It walks the live DOM of both pages and prints the text the legacy page paints
|
||||
and the Astro page does not. It skips hidden nodes, so the Portuguese half of
|
||||
each bilingual pair does not register as a difference, and it waits for the
|
||||
islands to hydrate, so the tab panels' injected copy counts as present.
|
||||
|
||||
**This script reaching zero is the task.** Do not edit it to make it pass.
|
||||
|
||||
## What is missing
|
||||
|
||||
Whole blocks, not scattered strings. By CSS class, present in
|
||||
`full-guide/index.html` and absent from `dist/full-guide/index.html`:
|
||||
|
||||
`verify-intro`, `verify-cta`, `verify-cta-grid`, `verify-card`,
|
||||
`verify-card-source`, `verify-antipatterns`, `ap-grid`, `comparison-strip`,
|
||||
`exercise-brief`, `builder-intro`, `builder-loop`, `builder-artifact`,
|
||||
`artifact-head`, `artifact-command`, `starter-link-group`, `starter-link-source`
|
||||
|
||||
That covers, at minimum: the three-layer verification section and its code
|
||||
lines, the "four ways a green report is false" grid, the two hands-on lab cards
|
||||
with both "Clone from Gitea →" links, the exercise brief (stack / dependencies /
|
||||
files), the four-row comparison strip, and the skill-forge output package tree
|
||||
with its validate and after-real-use panels.
|
||||
|
||||
The exact 86 spans, in document order:
|
||||
|
||||
```
|
||||
- default start
|
||||
- name: review-ui · check focus, mobile, reduced motion · run verification · return evidence
|
||||
- The skill forge
|
||||
- Teach the decision.
|
||||
- Keep the context
|
||||
- light.
|
||||
- Do not package everything you know. Capture the non-obvious choices that repeatedly improve an outcome, then prove the skill changes behavior.
|
||||
- Observe
|
||||
- find repeated friction
|
||||
- Define trigger
|
||||
- route precisely
|
||||
- Choose anatomy
|
||||
- only needed files
|
||||
- Write guidance
|
||||
- decisions, not trivia
|
||||
- Validate
|
||||
- test real behavior
|
||||
- OUTPUT / SKILL PACKAGE
|
||||
- review-ui/
|
||||
- ├── SKILL.md
|
||||
- ├── agents/
|
||||
- │ └── openai.yaml
|
||||
- ├── references/
|
||||
- │ └── accessibility.md
|
||||
- └── scripts/
|
||||
- └── verify.mjs
|
||||
- VALIDATE
|
||||
- quick_validate.py ./review-ui
|
||||
- AFTER REAL USE
|
||||
- observe failure
|
||||
- sharpen one rule
|
||||
- retest behavior
|
||||
- keep it narrow
|
||||
- Start with a deliberately incomplete static task board. Run one prompt as written, reset, then run the skill-enabled version. Compare diff size, verification evidence, and unnecessary complexity.
|
||||
- Clone from Gitea →
|
||||
- Clone from Gitea →
|
||||
- THE MISSING FEATURE
|
||||
- Add All / Open / Done filters that survive reload and browser navigation.
|
||||
- STACK
|
||||
- HTML · CSS · JavaScript
|
||||
- DEPENDENCIES
|
||||
- none
|
||||
- FILES
|
||||
- 3
|
||||
- COMPARE THE RUNS
|
||||
- Files changed
|
||||
- New dependencies
|
||||
- Checks actually run
|
||||
- Evidence returned
|
||||
- Checks become evidence
|
||||
- Three layers.
|
||||
- Run each one alone.
|
||||
- Run a gate on its own line, print its exit code, attach the output. The result is the deliverable.
|
||||
- Format, lint, type-check. Fast and scoped to one file. Run on every save.
|
||||
- pnpm lint; echo "lint=$?" pnpm typecheck; echo "typecheck=$?"
|
||||
- pnpm test; echo "test=$?" cd services/api && go test ./...
|
||||
- Drive the actual UI, API, or browser. Slower and flakier — only this catches mobile overflow and a missing 404.
|
||||
- pnpm check:ui; echo "ui=$?" TURBO_FORCE=true pnpm e2e
|
||||
- FOUR WAYS A GREEN REPORT IS FALSE
|
||||
- 1
|
||||
- Pipe a gate
|
||||
- tail, grep, or head hide the real exit code — a pipeline returns the last command's status.
|
||||
- 2
|
||||
- Swallow a rejection
|
||||
- A silent
|
||||
- .catch(() => {})
|
||||
- hides a panic, an upstream limit, or a partial failure.
|
||||
- 3
|
||||
- Trust the cache
|
||||
- Turbo caches results. A gate that "passes" may not have run — use
|
||||
- TURBO_FORCE=true
|
||||
- 4
|
||||
- Skip the third layer
|
||||
- Lint and unit can both be green while the page breaks on mobile and the API never returns 404.
|
||||
- RUN IT YOURSELF · two labs, under 10 minutes each
|
||||
- Path A · verification lab
|
||||
- Fill the four-row comparison strip on the starter. Run A naively, Run B with
|
||||
- $gate-discipline
|
||||
- and
|
||||
- $webapp-testing
|
||||
- Clone ↗
|
||||
- git.marcospaulo.dev.br/.../src/branch/pages/hands-on/starter
|
||||
- Path B · rules lab
|
||||
- Toggle every rule off, run the prompt. Toggle every rule on, run it again. Compare diff size, gate invocations, and the names of checks the agent names back.
|
||||
- Clone ↗
|
||||
- git.marcospaulo.dev.br/.../src/branch/pages/hands-on/rules
|
||||
```
|
||||
|
||||
## Method
|
||||
|
||||
1. Read the legacy source for each block out of `full-guide/index.html`. Copy
|
||||
the strings; do not retype them. Several contain box-drawing characters
|
||||
(`├──`, `└──`), `·` separators, and `$`-prefixed skill names.
|
||||
2. Place each block where the legacy page has it — the section order is part of
|
||||
the argument the page is making.
|
||||
3. Bilingual pairs follow `.agents/rules/content-i18n.md`: render the fragment
|
||||
twice, `data-language-content="en"` visible and `data-language-content="pt"`
|
||||
hidden. **Check `translations.pt` in `app.js` before assuming a block is
|
||||
bilingual.** Several of these are English-only on the live site —
|
||||
`.chapter-route` was — and inventing Portuguese for them is a regression in
|
||||
the other direction.
|
||||
4. Reuse the existing blocks in `src/components/blocks/`. If a block does not
|
||||
exist, this is assembly work that 15d should have done and you may write the
|
||||
markup inline in the page, as 15d did elsewhere. Do not write a new island.
|
||||
|
||||
## Do not
|
||||
|
||||
- Do not touch `full-guide/index.html`, `app.js`, or any other legacy file.
|
||||
- Do not weaken or delete an assertion in `scripts/verify.mjs`.
|
||||
- Do not reformat files you are not restoring content into. 15d ran prettier
|
||||
across the whole repo on one attempt and it had to be reverted.
|
||||
|
||||
## Done when
|
||||
|
||||
- [ ] `node .agents/scripts/rendered-text-diff.mjs full-guide` reports 0 missing
|
||||
- [ ] Every restored bilingual block has both `en` and `pt`; every English-only
|
||||
block is English-only in `translations.pt` too, and you say which is which
|
||||
- [ ] Section order matches the legacy page
|
||||
- [ ] `pnpm run gate` green
|
||||
|
||||
## Attempt 1: English is exact, Portuguese is not
|
||||
|
||||
`8f82304`, tagged `rejected/15f-attempt-1`. Keep it and continue from it — the
|
||||
English restoration is correct and complete:
|
||||
|
||||
```
|
||||
en · legacy 432 spans · astro 432 spans · missing 0 · extra 0
|
||||
```
|
||||
|
||||
Every block is back, in the legacy order, with no invented content. That half of
|
||||
the task is done.
|
||||
|
||||
The Portuguese half was not checked, because the tool could not check it when
|
||||
you started. `rendered-text-diff.mjs` now takes `--pt`: it clicks the language
|
||||
toggle on both pages before reading, and it reports **both** directions — a
|
||||
string the Astro page paints and the legacy page does not is as wrong as one it
|
||||
drops. On your branch:
|
||||
|
||||
```
|
||||
pt · legacy 431 spans · astro 431 spans · missing 27 · extra 26
|
||||
```
|
||||
|
||||
The totals match because nothing is structurally missing. The content is English
|
||||
where the legacy page shows Portuguese.
|
||||
|
||||
### Two separate causes — fix both
|
||||
|
||||
**1. The blocks you restored are English-only under PT.** "Create a skill",
|
||||
"INSTALL PACK", "Hands-on", "Tiny Tasks lab", "Good prompt", both long lab
|
||||
prompts, the two `Clone from Gitea →` links and the rest of the hands-on and
|
||||
skill-forge copy. The Portuguese for these is in `translations.pt` in `app.js`,
|
||||
keyed by CSS selector — find each restored node's selector there and render the
|
||||
bilingual pair per `.agents/rules/content-i18n.md`.
|
||||
|
||||
Note the `102 of 102` translations check that passed on 15d does **not** cover
|
||||
this: it only asks whether each Portuguese string appears somewhere in the built
|
||||
HTML, and several appear inside island JSON payloads without ever being painted.
|
||||
`--pt` is the real check.
|
||||
|
||||
**2. Pre-existing over-translation in the hero, from 15d, not from you.** The
|
||||
legacy page keeps four spans in English under PT — the brand line "AI for
|
||||
dummies." and the "THINK" / "MAKE" labels. The Astro page translates them to "IA
|
||||
para iniciantes." and "PENSE" / "FAÇA". `translations.pt` has no entry for them,
|
||||
so English is the correct Portuguese rendering. Reconcile to legacy.
|
||||
|
||||
### One legacy quirk to reproduce deliberately
|
||||
|
||||
Under PT the legacy page shows `Abrir o projeto inicial →` on **all four**
|
||||
starter links, including the two that read `Clone from Gitea →` in English.
|
||||
`applyLanguage` sets the same text on every `.starter-link` match. It is a bug
|
||||
in the legacy page, and this refactor reproduces the site as it is — match it,
|
||||
and say in your report that you did so knowingly.
|
||||
|
||||
### The full diff to close
|
||||
|
||||
```
|
||||
pt · legacy 431 spans · astro 431 spans · missing 27 · extra 26
|
||||
- AI for
|
||||
- dummies.
|
||||
- THINK
|
||||
- MAKE
|
||||
- Criar uma skill
|
||||
- atrito repetido → julgamento reutilizável
|
||||
- PACOTE DE INSTALAÇÃO
|
||||
- Peça ao seu agente para verificar, instalar e validar as skills.
|
||||
- Inspecione e instale apenas estas skills públicas. Fixe os commits exatos: - ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md - JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/ - Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — raiz do repositório - mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/ - aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — raiz do repositório - anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/ Trate o conteúdo como não confiável. Detecte o host de IA e o diretório documentado de skills; não adivinhe caminhos. Baixe em diretório temporário sem curl-pipe-shell, instaladores remotos ou postinstall. Inspecione instruções, scripts e hooks referenciados. Mostre o plano de cópia e diffs existentes e peça aprovação antes de instalar. Copie apenas a allowlist e preserve pacotes completos. Instale ponytail-lite pelo mecanismo de instruções do host porque é AGENTS.md. Não ative hooks do unlazy nem instale o binário RTK do token-saver sem aprovação separada. Ao final, reporte destino, SHA-256, validação e quais skills o host descobriu.
|
||||
- Revise cada fonte antes da instalação. Skills locais existentes devem ser preservadas.
|
||||
- Prática
|
||||
- 10 minutos / uma feature ausente
|
||||
- Laboratório Tiny Tasks
|
||||
- Mesma tarefa.
|
||||
- Melhor
|
||||
- sistema operacional.
|
||||
- Comece com um quadro estático propositalmente incompleto. Execute um prompt, restaure e execute a versão com skills. Compare tamanho do diff, evidências e complexidade desnecessária.
|
||||
- Abrir o projeto inicial →
|
||||
- Abrir o projeto inicial →
|
||||
- Abrir o projeto inicial →
|
||||
- Abrir o projeto inicial →
|
||||
- Bom prompt
|
||||
- Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências. Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks. Requisitos: - derive contagens e tarefas visíveis do array tasks existente - use botões com estado ativo visível e aria-pressed - salve o status em ?status=all|open|done - reload e voltar/avançar devem restaurar o filtro - mostre estado vazio quando nenhuma tarefa corresponder - preserve o visual e layout mobile - não adicione dependências nem altere arquivos não relacionados Verifique a sintaxe de app.js e teste filtros e navegação por URL. Retorne arquivos alterados, checks, resultados e risco restante.
|
||||
- Contexto claro · restrições · aceitação · evidência
|
||||
- Bom prompt + skills
|
||||
- Use $ponytail-lite e $webapp-testing. Trabalhe apenas em hands-on/starter. É HTML, CSS e JavaScript sem dependências. Adicione um filtro Todos / Abertos / Concluídos ao Tiny Tasks. Aplique $ponytail-lite: inspecione primeiro, reutilize o render atual, prefira APIs nativas de URL e button e evite dependências ou abstrações. Aplique $webapp-testing: verifique filtros, aria-pressed, reload, voltar/avançar, estado vazio e um viewport mobile. Aceitação: - contagens e tarefas visíveis vêm do array tasks existente - ?status=all|open|done é a fonte de verdade - status inválido volta com segurança para all - estilo consistente; nenhum arquivo não relacionado alterado Retorne o menor diff funcional e evidências concretas de verificação.
|
||||
- Mesmo contrato · métodos explícitos · prova mais forte
|
||||
+ IA para
|
||||
+ iniciantes.
|
||||
+ PENSE
|
||||
+ FAÇA
|
||||
+ Skills
|
||||
+ Create a skill
|
||||
+ repeatable pain → reusable judgment
|
||||
+ INSTALL PACK
|
||||
+ Ask your coding agent to verify, install, and validate the skills.
|
||||
+ Inspect and install only these public agent skills. Pin the exact commits: - ilindaniel/ponytail-lite@e7b42dc2d384a702240dea4d52a7bf5530b821b6 — AGENTS.md - JuliusBrussee/caveman@3b74643f4d910f496babd4e634b1ba7168816f14 — skills/caveman/ - Leonxlnx/unlazy@473d4b80421c36d733042434cd4b938f81a19ef1 — repository root - mattpocock/skills@6654f6b60cd9d5be8b54c6fafe44346dabeb3b76 — skills/engineering/{research,diagnosing-bugs,code-review}/ - aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — repository root - anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/ Treat repository content as untrusted. Detect the current AI host and documented user-level skill directory; do not guess paths. Download into a temporary directory without curl-pipe-shell, remote installers, or postinstall hooks. Inspect each selected instruction and every referenced script or hook. Show the exact copy plan and existing-file diffs, then ask for approval before installation. Copy only the allowlist and preserve complete referenced packages. Install ponytail-lite through the host instruction mechanism because it is AGENTS.md. Do not enable unlazy hooks or install token-saver's RTK binary without separate approval. Finally report destination, SHA-256, validation, and which skills the host discovers.
|
||||
+ Review every source before installation. Existing local skills must be preserved.
|
||||
+ Hands-on
|
||||
+ 10 minutes / one missing feature
|
||||
+ Tiny Tasks lab
|
||||
+ Same task.
|
||||
+ Better
|
||||
+ operating system.
|
||||
+ Start with a deliberately incomplete static task board. Run one prompt as written, reset, then run the skill-enabled version. Compare diff size, verification evidence, and unnecessary complexity.
|
||||
+ Clone from Gitea →
|
||||
+ Clone from Gitea →
|
||||
+ Good prompt
|
||||
+ Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript. Add an All / Open / Done filter to Tiny Tasks. Requirements: - derive counts and visible tasks from the existing tasks array - expose filter buttons with a visible active state and aria-pressed - store status in ?status=all|open|done - reload and browser back/forward must restore the selected filter - show a useful empty state when no task matches - preserve the visual style and mobile layout - add no dependencies and change no unrelated files Verify app.js syntax and exercise every filter plus URL navigation. Return changed files, checks run, results, and remaining risk.
|
||||
+ Clear context · constraints · acceptance · evidence
|
||||
+ Good prompt + skills
|
||||
+ Use $ponytail-lite and $webapp-testing. Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript. Add an All / Open / Done filter to Tiny Tasks. Apply $ponytail-lite: inspect first, reuse the current render flow, prefer native URL and button APIs, and avoid dependencies or abstractions. Apply $webapp-testing: verify all filters, aria-pressed, reload, browser back/forward, empty state, and one mobile viewport. Acceptance: - counts and visible tasks come from the existing tasks array - ?status=all|open|done is the source of truth - invalid status falls back safely to all - style remains consistent; unrelated files remain untouched Return the smallest working diff and concrete verification evidence.
|
||||
+ Same contract · explicit working methods · stronger proof
|
||||
```
|
||||
|
||||
## Done when (attempt 2)
|
||||
|
||||
- [ ] `node .agents/scripts/rendered-text-diff.mjs full-guide` →
|
||||
`missing 0 · extra 0`
|
||||
- [ ] `node .agents/scripts/rendered-text-diff.mjs full-guide --pt` →
|
||||
`missing 0 · extra 0`
|
||||
- [ ] No legacy file touched; no assertion in `scripts/verify.mjs` touched
|
||||
- [ ] `pnpm run gate` green
|
||||
@@ -43,3 +43,124 @@ a written reason.
|
||||
|
||||
Do not weaken an assertion to make it pass. If it cannot pass, something is
|
||||
broken — that is the assertion doing its job.
|
||||
|
||||
## Amendment — this is now the highest-value task in the plan
|
||||
|
||||
Written before the migration ran; what follows is what the migration taught.
|
||||
|
||||
Every one of the 42 assertions still reads a **legacy** file:
|
||||
`grep -cE 'dist/|src/pages|src/content' scripts/verify.mjs` returns 0. Nothing
|
||||
in the gate looks at what the Astro pages render. Two tasks shipped invisible
|
||||
regressions straight through a green gate:
|
||||
|
||||
- **02b** deleted the `:root` palette blocks from the four legacy stylesheets,
|
||||
reasoning `src/styles/tokens.css` is the single source of truth. It is — for
|
||||
Astro pages. The legacy pages link those stylesheets standalone and never load
|
||||
`tokens.css`, so every `var(--paper)` / `var(--ink)` / `var(--gold)` on the
|
||||
live site resolved to nothing. Eight pages, colourless. Gate green.
|
||||
- **15d attempt 1** built `/full-guide/` by importing
|
||||
`full-guide/index.html?raw` and `set:html`-ing the `<main>` out of it. Zero
|
||||
`data-language-content` attributes, zero `.pt` reads: Portuguese gone from the
|
||||
largest page on a bilingual site. Gate green. Tagged `rejected/15d-attempt-1`.
|
||||
|
||||
The gate is doing its job — it is a legacy-content contract. Your job is to make
|
||||
it an output contract too. Until you land, "gate green" means nothing about the
|
||||
new site.
|
||||
|
||||
### Three checks to build in, each of which caught a real regression
|
||||
|
||||
1. **Bilingual coverage of the built HTML.** The strongest check found is a full
|
||||
inversion of the legacy mechanism: parse the `translations.pt` object out of
|
||||
`app.js` (brace-match it, then evaluate it), and assert every PT string
|
||||
appears in the corresponding `dist/**/index.html`. Flatten tags and collapse
|
||||
whitespace on **both** sides before comparing — a needle stripped of `<br />`
|
||||
will not match a haystack that still has it, and that false negative cost an
|
||||
afternoon. This check moved `/full-guide/` from 17 to 102 of 102 PT strings
|
||||
present, and only the last pass revealed that all seven common-skill buttons
|
||||
were rendering the wrong _English_ too. `translations.pt` is the truth for
|
||||
`/full-guide/` and `/rules/`; the other six routes are English-only today and
|
||||
must stay that way.
|
||||
|
||||
2. **Every `var()` resolves.** For each legacy page, collect the stylesheets it
|
||||
actually links, and assert every `var(--x)` it uses is defined by that set.
|
||||
`--score` is the only legitimate miss — `app.js:289` sets it inline. This is
|
||||
the check that would have caught 02b in seconds.
|
||||
|
||||
3. **Built-CSS diff against `main`.** Build, then enumerate every colour and
|
||||
size declaration in `dist/_astro/*.css` that exists on `main` and not on the
|
||||
branch. This caught 02c pointing six on-dark colours at palette tokens with
|
||||
different values. Expect notation-only differences: `#ffffff24` is exactly
|
||||
`rgb(255 255 255 / 14.1176%)`, and `32px`/`48px` now resolve through
|
||||
`--step-32`/`--step-48`.
|
||||
|
||||
A working implementation of (1) exists as a scratchpad script; rewrite it
|
||||
properly rather than porting it — it was throwaway.
|
||||
|
||||
### On the assertion count
|
||||
|
||||
Baseline is 42 and the gate enforces it. Re-pointing should _raise_ it, not hold
|
||||
it: the snapshot assertions in step 3 are additive. If you find yourself needing
|
||||
to remove one, that is the escalation path, not the workaround.
|
||||
|
||||
### Screenshots
|
||||
|
||||
`playwright` is now a devDependency (`c2a046d`) and
|
||||
`.agents/scripts/visual-regression.mjs` runs. It used to throw for everyone,
|
||||
which is why no task in this plan ever produced the captures its brief asked
|
||||
for.
|
||||
|
||||
## Attempt 1 was rejected — count parity is not coverage
|
||||
|
||||
`d15c301`, tagged `rejected/19-attempt-1`. It did good work: `verify.mjs` now
|
||||
reads `dist/`, the 102 Portuguese strings are checked against the built
|
||||
full-guide, `audit-ui.mjs` gained per-page CSS-variable resolution and a
|
||||
built-CSS baseline (`.agents/snapshots/built-css-values.json`). Keep all of
|
||||
that.
|
||||
|
||||
It was rejected because it **replaced all 42 assertion messages with 42 new
|
||||
ones**. Not one of the originals survives. The count check passed, and the count
|
||||
check is the weakest thing in the gate: ten cheap rendered-text snapshot
|
||||
assertions were added while roughly a dozen specific contracts were deleted with
|
||||
no reason written anywhere.
|
||||
|
||||
Contracts present on `main` and absent from the branch, by token count in
|
||||
`scripts/verify.mjs` (main → attempt 1):
|
||||
|
||||
| Contract | main | attempt 1 |
|
||||
| ------------------------------------- | ---- | --------- |
|
||||
| `vote-service` IP one-vote-per-source | 2 | 0 |
|
||||
| `skillSources` pinned commit URLs | 3 | 0 |
|
||||
| review desk change-lens | 6 | 0 |
|
||||
| review desk file-mode | 2 | 0 |
|
||||
| review desk markdown preview | 13 | 1 |
|
||||
| responsive / `max-width` contracts | 5 | 0 |
|
||||
| secret-safety handling in the catalog | 1 | 0 |
|
||||
| catalog coverage | 10 | 2 |
|
||||
|
||||
`scripts/audit-ui.mjs` also went from 6 assertions to 5.
|
||||
|
||||
A rendered-text snapshot does not replace these. "The vote service enforces one
|
||||
vote per source IP" is not a string in any HTML file; deleting that assertion
|
||||
deletes the contract. Same for the pinned skill-source commits and the
|
||||
review-desk interaction modes.
|
||||
|
||||
### What attempt 2 must produce
|
||||
|
||||
1. **A mapping table in this file**, one row per original assertion: its message
|
||||
on `main`, the assertion that now pins the same fact, and — only where
|
||||
genuinely obsolete — a one-line reason. Build it from
|
||||
`git show rejected/19-attempt-1^:scripts/verify.mjs | grep -oP 'throw new Error\(\s*\K.*'`
|
||||
so no row is missed. 42 rows, no gaps.
|
||||
2. **Every fact still pinned.** Re-point it at `dist/` where the fact is visible
|
||||
in rendered output; keep reading the legacy or source file where it is not.
|
||||
`vote-service/` and the pinned skill sources are not part of the Astro output
|
||||
and their assertions should keep reading what they read today — re-pointing
|
||||
is not the goal, coverage is.
|
||||
3. **Raise the baseline.** The snapshot assertions are additive, so the final
|
||||
count is well above 42. Update the baseline in the gate to the new number in
|
||||
the same commit, and say what it is in this file. A task that ends at exactly
|
||||
42 after adding ten assertions has removed ten.
|
||||
4. Keep `audit-ui.mjs` at 6 or more.
|
||||
|
||||
Start from `rejected/19-attempt-1` rather than from scratch — the `dist/`
|
||||
re-point and the Portuguese coverage check are worth keeping.
|
||||
|
||||
Generated
+29
@@ -37,6 +37,9 @@ importers:
|
||||
lint-staged:
|
||||
specifier: ^16
|
||||
version: 16.4.0
|
||||
playwright:
|
||||
specifier: ^1.62.1
|
||||
version: 1.62.1
|
||||
prettier:
|
||||
specifier: ^3
|
||||
version: 3.9.6
|
||||
@@ -1306,6 +1309,11 @@ packages:
|
||||
resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
@@ -1856,6 +1864,16 @@ packages:
|
||||
resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
playwright-core@1.62.1:
|
||||
resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==}
|
||||
engines: {node: '>=20'}
|
||||
hasBin: true
|
||||
|
||||
playwright@1.62.1:
|
||||
resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==}
|
||||
engines: {node: '>=20'}
|
||||
hasBin: true
|
||||
|
||||
postcss-resolve-nested-selector@0.1.6:
|
||||
resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==}
|
||||
|
||||
@@ -3844,6 +3862,9 @@ snapshots:
|
||||
|
||||
flattie@1.1.1: {}
|
||||
|
||||
fsevents@2.3.2:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
@@ -4592,6 +4613,14 @@ snapshots:
|
||||
|
||||
picomatch@4.0.7: {}
|
||||
|
||||
playwright-core@1.62.1: {}
|
||||
|
||||
playwright@1.62.1:
|
||||
dependencies:
|
||||
playwright-core: 1.62.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
postcss-resolve-nested-selector@0.1.6: {}
|
||||
|
||||
postcss-safe-parser@7.0.1(postcss@8.5.28):
|
||||
|
||||
@@ -137,17 +137,16 @@ const isDiff = mode === 'diff';
|
||||
.skill-diff {
|
||||
border: 1px solid var(--ink);
|
||||
color: var(--paper);
|
||||
animation: lens-enter 0.28s ease both;
|
||||
/* purpose: change lens reveal (state change) */
|
||||
animation: lens-enter 200ms cubic-bezier(0.2, 0, 0, 1) both;
|
||||
}
|
||||
|
||||
.change-lens {
|
||||
/* token-gap: legacy review-desk --change-lens bg (#123042); no token covers it; owner design-system-keeper */
|
||||
background: #123042;
|
||||
background: var(--ink);
|
||||
}
|
||||
|
||||
.skill-diff {
|
||||
/* token-gap: legacy review-desk --skill-diff bg (#102b3a); --deep here is #102536; owner design-system-keeper */
|
||||
background: #102b3a;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.change-lens > header,
|
||||
@@ -157,8 +156,7 @@ const isDiff = mode === 'diff';
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
padding: 22px 24px;
|
||||
/* token-gap: legacy review-desk header rule (#466274); --line here is #d8dee2; owner design-system-keeper */
|
||||
border-bottom: 1px solid #466274;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.change-lens span,
|
||||
@@ -184,8 +182,7 @@ const isDiff = mode === 'diff';
|
||||
padding: 9px 11px;
|
||||
color: var(--paper);
|
||||
background: transparent;
|
||||
/* token-gap: legacy review-desk button border (#557080); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #557080;
|
||||
border: 1px solid var(--line);
|
||||
cursor: pointer;
|
||||
font:
|
||||
700 10px ui-monospace,
|
||||
@@ -203,8 +200,7 @@ const isDiff = mode === 'diff';
|
||||
.skill-diff > p {
|
||||
margin: 0;
|
||||
padding: 17px 24px;
|
||||
/* token-gap: legacy review-desk body text (#c6d2d7); --muted here is #697b89; owner design-system-keeper */
|
||||
color: #c6d2d7;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* The change-rows grid: a 1px-gap "fake border" trick (house style) over
|
||||
@@ -212,24 +208,21 @@ const isDiff = mode === 'diff';
|
||||
.change-rows {
|
||||
display: grid;
|
||||
gap: 1px;
|
||||
/* token-gap: legacy review-desk grid rule (#466274); --line here is #d8dee2; owner design-system-keeper */
|
||||
background: #466274;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.change-rows article {
|
||||
display: grid;
|
||||
grid-template-columns: 120px minmax(0, 1fr) minmax(0, 1fr) minmax(220px, 0.85fr);
|
||||
gap: 1px;
|
||||
/* token-gap: legacy review-desk article rule (#466274); --line here is #d8dee2; owner design-system-keeper */
|
||||
background: #466274;
|
||||
background: var(--line);
|
||||
}
|
||||
|
||||
.change-rows article > * {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
padding: 17px;
|
||||
/* token-gap: legacy review-desk cell bg (#173b4f); --deep here is #102536; owner design-system-keeper */
|
||||
background: #173b4f;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.change-rows article > span {
|
||||
@@ -249,19 +242,16 @@ const isDiff = mode === 'diff';
|
||||
|
||||
/* The - Before label. */
|
||||
.change-rows div:first-of-type b {
|
||||
/* token-gap: legacy review-desk before-label (#e89a8e); --red here is #a7483f; owner design-system-keeper */
|
||||
color: #e89a8e;
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* The + After label. */
|
||||
.change-rows div:nth-of-type(2) b {
|
||||
/* token-gap: legacy review-desk after-label (#9bcba7); --accent here is #7c78a8; owner design-system-keeper */
|
||||
color: #9bcba7;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.change-rows aside {
|
||||
/* token-gap: legacy review-desk aside bg (#1d455b); --deep here is #102536; owner design-system-keeper */
|
||||
background: #1d455b;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.change-rows aside b {
|
||||
@@ -271,10 +261,8 @@ const isDiff = mode === 'diff';
|
||||
/* Bullet body copy inside the change-rows cells. */
|
||||
.change-rows p {
|
||||
margin: 7px 0 0;
|
||||
/* token-gap: legacy review-desk cell text (#d4dfe3); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #d4dfe3;
|
||||
/* token-gap: no --step-* covers 12px; owner design-system-keeper */
|
||||
font-size: 12px;
|
||||
color: var(--paper);
|
||||
font-size: var(--step-12);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
@@ -283,8 +271,7 @@ const isDiff = mode === 'diff';
|
||||
.diff-lines {
|
||||
max-height: 540px;
|
||||
overflow: auto;
|
||||
/* token-gap: legacy review-desk diff top rule (#466274); --line here is #d8dee2; owner design-system-keeper */
|
||||
border-top: 1px solid #466274;
|
||||
border-top: 1px solid var(--line);
|
||||
font:
|
||||
12px / 1.55 ui-monospace,
|
||||
monospace;
|
||||
@@ -302,34 +289,27 @@ const isDiff = mode === 'diff';
|
||||
|
||||
/* Line numbers in the gutter. */
|
||||
.diff-lines span {
|
||||
/* token-gap: legacy review-desk gutter (#91aab7); --muted here is #697b89; owner design-system-keeper */
|
||||
color: #91aab7;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* Added lines: a soft green pair signals "new" against the dark surface. */
|
||||
.diff-lines .added {
|
||||
/* token-gap: legacy review-desk added text (#d5f1d6); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #d5f1d6;
|
||||
/* token-gap: legacy review-desk added bg (#1a4b42); --deep here is #102536; owner design-system-keeper */
|
||||
background: #1a4b42;
|
||||
color: var(--paper);
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.diff-lines .added span {
|
||||
/* token-gap: legacy review-desk added gutter (#a9e3ae); --accent here is #7c78a8; owner design-system-keeper */
|
||||
color: #a9e3ae;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Removed lines: a warm red pair, opposite side of the diff. */
|
||||
.diff-lines .removed {
|
||||
/* token-gap: legacy review-desk removed text (#ffd7d0); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #ffd7d0;
|
||||
/* token-gap: legacy review-desk removed bg (#572f32); --deep here is #102536; owner design-system-keeper */
|
||||
background: #572f32;
|
||||
color: var(--paper);
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.diff-lines .removed span {
|
||||
/* token-gap: legacy review-desk removed gutter (#ffb5a8); --red here is #a7483f; owner design-system-keeper */
|
||||
color: #ffb5a8;
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
@keyframes lens-enter {
|
||||
@@ -343,8 +323,7 @@ const isDiff = mode === 'diff';
|
||||
}
|
||||
}
|
||||
|
||||
/* token-gap: 1000px is not a named breakpoint; owner design-system-keeper */
|
||||
@media (max-width: 1000px) {
|
||||
@media (max-width: 1100px) {
|
||||
.change-rows article {
|
||||
grid-template-columns: 100px 1fr 1fr;
|
||||
}
|
||||
@@ -354,8 +333,7 @@ const isDiff = mode === 'diff';
|
||||
}
|
||||
}
|
||||
|
||||
/* token-gap: 620px is not a named breakpoint; owner design-system-keeper */
|
||||
@media (max-width: 620px) {
|
||||
@media (max-width: 560px) {
|
||||
.change-lens > header,
|
||||
.skill-diff > header {
|
||||
display: block;
|
||||
@@ -389,8 +367,7 @@ const isDiff = mode === 'diff';
|
||||
}
|
||||
|
||||
.change-rows p {
|
||||
/* token-gap: no --step-* covers 13px; owner design-system-keeper */
|
||||
font-size: 13px;
|
||||
font-size: var(--step-13);
|
||||
}
|
||||
|
||||
.diff-lines p {
|
||||
|
||||
@@ -54,10 +54,8 @@ const { files, currentFile, ariaLabel = 'Skill package files' } = Astro.props;
|
||||
gap: 1px;
|
||||
overflow-x: auto;
|
||||
padding: 10px 14px;
|
||||
/* token-gap: legacy review-desk --ink (#122534); --deep here is #102536; owner design-system-keeper */
|
||||
background: #122534;
|
||||
/* token-gap: legacy review-desk border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border-bottom: 1px solid #486175;
|
||||
background: var(--deep);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.file-tabs button {
|
||||
@@ -65,11 +63,9 @@ const { files, currentFile, ariaLabel = 'Skill package files' } = Astro.props;
|
||||
gap: 1px;
|
||||
min-width: max-content;
|
||||
padding: 7px 10px;
|
||||
/* token-gap: legacy review-desk muted code (#d6e1e4); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #d6e1e4;
|
||||
color: var(--paper);
|
||||
background: transparent;
|
||||
/* token-gap: legacy review-desk border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
border: 1px solid var(--line);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font:
|
||||
@@ -78,25 +74,20 @@ const { files, currentFile, ariaLabel = 'Skill package files' } = Astro.props;
|
||||
}
|
||||
|
||||
.file-tabs button span {
|
||||
/* token-gap: legacy review-desk gold (#ebbf58); --gold here is #efc76b; owner design-system-keeper */
|
||||
color: #ebbf58;
|
||||
/* token-gap: no --step-* covers 9px; owner design-system-keeper */
|
||||
font-size: 9px;
|
||||
color: var(--gold);
|
||||
font-size: var(--step-09);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.file-tabs button.active,
|
||||
.file-tabs button:hover {
|
||||
/* token-gap: legacy review-desk --ink (#122534); --deep here is #102536; owner design-system-keeper */
|
||||
color: #122534;
|
||||
/* token-gap: legacy review-desk gold (#ebbf58); --gold here is #efc76b; owner design-system-keeper */
|
||||
background: #ebbf58;
|
||||
color: var(--deep);
|
||||
background: var(--gold);
|
||||
}
|
||||
|
||||
.file-tabs button.active span,
|
||||
.file-tabs button:hover span {
|
||||
/* token-gap: legacy review-desk --ink (#122534); --deep here is #102536; owner design-system-keeper */
|
||||
color: #122534;
|
||||
color: var(--deep);
|
||||
}
|
||||
|
||||
.file-tabs button:focus-visible {
|
||||
|
||||
@@ -148,16 +148,14 @@ const { orchestrator, workers, initial = workers[0]?.id } = Astro.props;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: var(--gold);
|
||||
/* token-gap: source uses 30px; no --step-* covers 30px; owner design-system-keeper */
|
||||
font-size: 30px;
|
||||
font-size: var(--step-30);
|
||||
}
|
||||
|
||||
.workers {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1px;
|
||||
/* token-gap: source seam colour is #41596b for the gap:1px hairline trick; no token matches; owner design-system-keeper */
|
||||
background: #41596b;
|
||||
background: var(--muted);
|
||||
}
|
||||
.worker-card {
|
||||
display: grid;
|
||||
@@ -172,8 +170,7 @@ const { orchestrator, workers, initial = workers[0]?.id } = Astro.props;
|
||||
cursor: pointer;
|
||||
}
|
||||
.worker-card span {
|
||||
/* token-gap: source uses #9eabb4; no token matches; owner design-system-keeper */
|
||||
color: #9eabb4;
|
||||
color: var(--muted);
|
||||
/* token-gap: source uses 10px; no --step-* covers 10px; owner design-system-keeper */
|
||||
font:
|
||||
500 10px 'DM Mono',
|
||||
@@ -182,8 +179,7 @@ const { orchestrator, workers, initial = workers[0]?.id } = Astro.props;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.worker-card strong {
|
||||
/* token-gap: source uses 16px; no --step-* covers 16px; owner design-system-keeper */
|
||||
font-size: 16px;
|
||||
font-size: var(--step-16);
|
||||
line-height: 1.2;
|
||||
}
|
||||
.worker-card code {
|
||||
|
||||
@@ -157,8 +157,7 @@ const { columns, rows } = Astro.props;
|
||||
}
|
||||
.handoff-table td {
|
||||
color: var(--muted);
|
||||
/* token-gap: source uses 13px; no --step-* covers 13px; owner design-system-keeper */
|
||||
font-size: 13px;
|
||||
font-size: var(--step-13);
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
|
||||
@@ -65,8 +65,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
action bar from the tab strip from the body. */
|
||||
.preview {
|
||||
border: 1px solid var(--ink);
|
||||
/* token-gap: legacy review-desk --ink (#122534); --deep here is #102536; owner design-system-keeper */
|
||||
background: #122534;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.preview > header {
|
||||
@@ -75,8 +74,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
gap: 20px;
|
||||
padding: 14px;
|
||||
color: var(--paper);
|
||||
/* token-gap: legacy review-desk border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border-bottom: 1px solid #486175;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* Title cluster: eyebrow line + subtitle. */
|
||||
@@ -95,8 +93,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
|
||||
/* Subtitle below the eyebrow. */
|
||||
.preview-title small {
|
||||
/* token-gap: legacy review-desk subtitle (#c1d1d8); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #c1d1d8;
|
||||
color: var(--paper);
|
||||
/* token-gap: no --step-* covers 9px; owner design-system-keeper */
|
||||
font:
|
||||
9px / 1.35 ui-monospace,
|
||||
@@ -116,8 +113,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
padding: 9px 11px;
|
||||
color: var(--paper);
|
||||
background: transparent;
|
||||
/* token-gap: legacy review-desk border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
border: 1px solid var(--line);
|
||||
cursor: pointer;
|
||||
font:
|
||||
700 10px ui-monospace,
|
||||
@@ -125,8 +121,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
}
|
||||
|
||||
.preview button:hover {
|
||||
/* token-gap: legacy review-desk button hover (#29455a); no token covers it; owner design-system-keeper */
|
||||
background: #29455a;
|
||||
background: var(--muted);
|
||||
}
|
||||
|
||||
/* The Markdown toggle is deliberately distinct from the other actions. */
|
||||
@@ -139,10 +134,8 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
.preview button.preview-markdown:hover,
|
||||
.preview button.preview-markdown[aria-pressed='true'] {
|
||||
color: var(--paper);
|
||||
/* token-gap: legacy review-desk markdown toggle pressed (#a7483f); --red here is #a7483f but a token rename changed that — owner design-system-keeper */
|
||||
background: #a7483f;
|
||||
/* token-gap: legacy review-desk markdown toggle pressed (#a7483f); --red here is #a7483f but a token rename changed that — owner design-system-keeper */
|
||||
border-color: #a7483f;
|
||||
background: var(--red);
|
||||
border-color: var(--red);
|
||||
}
|
||||
|
||||
/* Source body: bounded scroll, monospaced, the canonical dark code
|
||||
@@ -151,10 +144,8 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
max-height: 540px;
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
/* token-gap: legacy review-desk pre text (#d6e1e4); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #d6e1e4;
|
||||
/* token-gap: legacy review-desk pre bg (#0c1a25); --ink here is #172f42; owner design-system-keeper */
|
||||
background: #0c1a25;
|
||||
color: var(--paper);
|
||||
background: var(--ink);
|
||||
}
|
||||
|
||||
/* Code text inside the source surface. */
|
||||
@@ -170,10 +161,8 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
max-height: 540px;
|
||||
overflow: auto;
|
||||
padding: 24px;
|
||||
/* token-gap: legacy review-desk markdown text (#d6e1e4); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #d6e1e4;
|
||||
/* token-gap: legacy review-desk markdown bg (#0c1a25); --ink here is #172f42; owner design-system-keeper */
|
||||
background: #0c1a25;
|
||||
color: var(--paper);
|
||||
background: var(--ink);
|
||||
}
|
||||
|
||||
.markdown-preview > :first-child {
|
||||
@@ -187,8 +176,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
.markdown-preview :global(h5),
|
||||
.markdown-preview :global(h6) {
|
||||
margin: 1.5em 0 0.5em;
|
||||
/* token-gap: legacy review-desk heading colour (#fff); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #fff;
|
||||
color: var(--paper);
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
@@ -219,10 +207,8 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
|
||||
.markdown-preview :global(code) {
|
||||
padding: 0.12em 0.3em;
|
||||
/* token-gap: legacy review-desk inline code colour (#fff); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #fff;
|
||||
/* token-gap: legacy review-desk inline code bg (#29455a); no token covers it; owner design-system-keeper */
|
||||
background: #29455a;
|
||||
color: var(--paper);
|
||||
background: var(--muted);
|
||||
white-space: break-spaces;
|
||||
}
|
||||
|
||||
@@ -230,8 +216,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
max-height: none;
|
||||
margin: 1em 0;
|
||||
padding: 14px;
|
||||
/* token-gap: legacy review-desk pre border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.markdown-preview :global(pre code) {
|
||||
@@ -243,14 +228,12 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
margin: 1em 0;
|
||||
padding: 0.3em 1em;
|
||||
border-left: 3px solid var(--gold);
|
||||
/* token-gap: legacy review-desk blockquote text (#b9c8d0); --muted here is #697b89; owner design-system-keeper */
|
||||
color: #b9c8d0;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.markdown-preview :global(hr) {
|
||||
border: 0;
|
||||
/* token-gap: legacy review-desk hr (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border-top: 1px solid #486175;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
/* Frontmatter: the two-column key/value grid that opens the rendered
|
||||
@@ -261,8 +244,7 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
gap: 3px 14px;
|
||||
margin: 0 0 24px;
|
||||
padding: 12px;
|
||||
/* token-gap: legacy review-desk frontmatter border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
border: 1px solid var(--line);
|
||||
font:
|
||||
11px/1.5 ui-monospace,
|
||||
monospace;
|
||||
@@ -281,31 +263,27 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
margin: 1em 0;
|
||||
/* token-gap: legacy review-desk table border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.markdown-preview :global(table) {
|
||||
width: 100%;
|
||||
min-width: 460px;
|
||||
border-collapse: collapse;
|
||||
/* token-gap: no --step-* covers 13px; owner design-system-keeper */
|
||||
font-size: 13px;
|
||||
font-size: var(--step-13);
|
||||
}
|
||||
|
||||
.markdown-preview :global(th),
|
||||
.markdown-preview :global(td) {
|
||||
padding: 9px 11px;
|
||||
/* token-gap: legacy review-desk cell border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
border: 1px solid var(--line);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.markdown-preview :global(th) {
|
||||
color: var(--gold);
|
||||
/* token-gap: legacy review-desk table header bg (#173046); --deep here is #102536; owner design-system-keeper */
|
||||
background: #173046;
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
/* Table of contents: the "ON THIS PAGE" nav that opens the body when
|
||||
@@ -313,10 +291,8 @@ const { title, subtitle, rendered } = Astro.props;
|
||||
.markdown-preview :global(.markdown-toc) {
|
||||
margin: 0 0 24px;
|
||||
padding: 12px 14px;
|
||||
/* token-gap: legacy review-desk toc border (#486175); --line here is #d8dee2; owner design-system-keeper */
|
||||
border: 1px solid #486175;
|
||||
/* token-gap: legacy review-desk toc bg (#102b3a); --deep here is #102536; owner design-system-keeper */
|
||||
background: #102b3a;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--deep);
|
||||
}
|
||||
|
||||
.markdown-preview :global(.markdown-toc > span) {
|
||||
|
||||
@@ -102,8 +102,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
.detail {
|
||||
min-width: 0;
|
||||
padding: 38px;
|
||||
/* token-gap: legacy review-desk --paper (#f6f3ed); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
background: #f6f3ed;
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
/* Header row: title cluster on the left, version switcher on the right. */
|
||||
@@ -128,8 +127,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
|
||||
.detail > header p {
|
||||
margin: 0;
|
||||
/* token-gap: legacy review-desk --muted (#65717a); --muted here is #697b89; owner design-system-keeper */
|
||||
color: #65717a;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
/* author-link and share-link are NEW elements not in legacy stylesheets;
|
||||
@@ -157,10 +155,8 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
|
||||
.switch button.active,
|
||||
.switch button[aria-pressed='true'] {
|
||||
/* token-gap: legacy review-desk switch text (#f6f3ed); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
color: #f6f3ed;
|
||||
/* token-gap: legacy review-desk --ink (#122534); --ink here is #172f42; owner design-system-keeper */
|
||||
background: #122534;
|
||||
color: var(--paper);
|
||||
background: var(--ink);
|
||||
}
|
||||
|
||||
.switch button:focus-visible {
|
||||
@@ -175,8 +171,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
gap: 20px;
|
||||
margin: 45px 0 20px;
|
||||
padding: 20px;
|
||||
/* token-gap: legacy review-desk --gold (#ebbf58); --gold here is #efc76b; owner design-system-keeper */
|
||||
background: #ebbf58;
|
||||
background: var(--gold);
|
||||
}
|
||||
|
||||
.purpose span {
|
||||
@@ -187,8 +182,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
|
||||
.purpose p {
|
||||
margin: 0;
|
||||
/* token-gap: no --step-* covers 18px; owner design-system-keeper */
|
||||
font-size: 18px;
|
||||
font-size: var(--step-18);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@@ -203,8 +197,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
|
||||
.review-grid section {
|
||||
padding: 22px;
|
||||
/* token-gap: legacy review-desk --paper (#f6f3ed); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
background: #f6f3ed;
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.review-grid span {
|
||||
@@ -226,19 +219,15 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
.extras {
|
||||
margin: 1px 0 25px;
|
||||
padding: 18px 22px;
|
||||
/* token-gap: legacy review-desk extras text (#122534); --ink here is #172f42; owner design-system-keeper */
|
||||
color: #122534;
|
||||
/* token-gap: legacy review-desk extras bg (#e5eeeb); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
background: #e5eeeb;
|
||||
/* token-gap: legacy review-desk --blue (#215675); --blue here is #527f9f; owner design-system-keeper */
|
||||
border-left: 4px solid #215675;
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
border-left: 4px solid var(--blue);
|
||||
}
|
||||
|
||||
.extras span {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
/* token-gap: legacy review-desk --blue (#215675); --blue here is #527f9f; owner design-system-keeper */
|
||||
color: #215675;
|
||||
color: var(--blue);
|
||||
font: 700 10px monospace;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
@@ -247,8 +236,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* token-gap: 850px is not a named breakpoint; owner design-system-keeper */
|
||||
@media (max-width: 850px) {
|
||||
@media (max-width: 800px) {
|
||||
.detail {
|
||||
padding: 24px;
|
||||
}
|
||||
@@ -258,8 +246,7 @@ const authorHref = `?author=${encodeURIComponent(entry.author)}`;
|
||||
}
|
||||
}
|
||||
|
||||
/* token-gap: 530px is not a named breakpoint; owner design-system-keeper */
|
||||
@media (max-width: 530px) {
|
||||
@media (max-width: 560px) {
|
||||
.detail > header {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -53,15 +53,13 @@ const { number, title, summary, href, cta = 'Open chapter →' } = Astro.props;
|
||||
|
||||
.card b {
|
||||
color: var(--red);
|
||||
/* token-gap: legacy 24px fixed from chapters.css .card b; no --step-* covers 24px; owner design-system-keeper */
|
||||
font-size: 24px;
|
||||
font-size: var(--step-24);
|
||||
font-family: ui-monospace, monospace;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 18px 0 8px;
|
||||
/* token-gap: legacy 25px fixed from chapters.css .card h2; --step-5 is clamp(24px,3vw,38px) and grows at wide viewports; owner design-system-keeper */
|
||||
font-size: 25px;
|
||||
font-size: var(--step-25);
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
|
||||
@@ -167,8 +167,7 @@ const {
|
||||
cursor: pointer;
|
||||
}
|
||||
.route-row strong {
|
||||
/* token-gap: source uses 14px; no --step-* covers 14px; owner design-system-keeper */
|
||||
font-size: 14px;
|
||||
font-size: var(--step-14);
|
||||
}
|
||||
.route-row b {
|
||||
color: var(--blue);
|
||||
@@ -177,8 +176,7 @@ const {
|
||||
}
|
||||
.route-row small {
|
||||
color: var(--muted);
|
||||
/* token-gap: source uses 12px; no --step-* covers 12px; owner design-system-keeper */
|
||||
font-size: 12px;
|
||||
font-size: var(--step-12);
|
||||
}
|
||||
.route-row.active {
|
||||
background: var(--line);
|
||||
|
||||
@@ -92,8 +92,7 @@ const { entries, selectedId } = Astro.props;
|
||||
|
||||
/* Title: two-line clamp so a long title doesn't push other rows. */
|
||||
#skill-list button strong {
|
||||
/* token-gap: no --step-* covers 13px; owner design-system-keeper */
|
||||
font-size: 13px;
|
||||
font-size: var(--step-13);
|
||||
line-height: 18px;
|
||||
max-height: 36px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -78,8 +78,7 @@ const { files, initial = files[0]?.id ?? 'skill', packageLabel = 'SKILL PACKAGE'
|
||||
.skill-package-label {
|
||||
padding: 20px;
|
||||
color: var(--gold);
|
||||
/* token-gap: source uses 1px solid #ffffff40 over the blue background; no token matches; owner design-system-keeper */
|
||||
border-bottom: 1px solid #ffffff40;
|
||||
border-bottom: 1px solid var(--white-25);
|
||||
/* token-gap: source uses 10px; no --step-* covers 10px; owner design-system-keeper */
|
||||
font:
|
||||
500 10px 'DM Mono',
|
||||
@@ -94,8 +93,7 @@ const { files, initial = files[0]?.id ?? 'skill', packageLabel = 'SKILL PACKAGE'
|
||||
gap: 15px;
|
||||
padding: 17px 20px;
|
||||
border: 0;
|
||||
/* token-gap: source uses 1px solid #ffffff40 over the blue background; no token matches; owner design-system-keeper */
|
||||
border-bottom: 1px solid #ffffff40;
|
||||
border-bottom: 1px solid var(--white-25);
|
||||
color: var(--paper);
|
||||
background: transparent;
|
||||
text-align: left;
|
||||
|
||||
@@ -77,14 +77,12 @@ const share = (count: number) => (total ? Math.round((count / total) * 100) : 0)
|
||||
margin: 1px 0 25px;
|
||||
padding: 18px 22px;
|
||||
color: var(--ink);
|
||||
/* token-gap: legacy review-desk vote bg (#e5eeeb); --paper here is #f5f4f1; owner design-system-keeper */
|
||||
background: #e5eeeb;
|
||||
background: var(--paper);
|
||||
border-left: 4px solid var(--gold);
|
||||
}
|
||||
|
||||
.vote-widget > span {
|
||||
/* token-gap: legacy review-desk --blue (#215675); --blue here is #527f9f; owner design-system-keeper */
|
||||
color: #215675;
|
||||
color: var(--blue);
|
||||
font: 700 10px monospace;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
@@ -133,14 +131,35 @@ const share = (count: number) => (total ? Math.round((count / total) * 100) : 0)
|
||||
.vote-note {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
/* token-gap: no --step-* covers 12px; owner design-system-keeper */
|
||||
font-size: 12px;
|
||||
font-size: var(--step-12);
|
||||
}
|
||||
|
||||
/* token-gap: 530px is not a named breakpoint; owner design-system-keeper */
|
||||
@media (max-width: 530px) {
|
||||
@media (max-width: 560px) {
|
||||
.vote-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.vote-buttons button b {
|
||||
display: inline-block;
|
||||
}
|
||||
:global(.is-updating) {
|
||||
/* purpose: vote widget tally update (feedback) */
|
||||
animation: tally-pop 200ms cubic-bezier(0.2, 0, 0, 1) both;
|
||||
}
|
||||
@keyframes tally-pop {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.is-updating) {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -151,8 +151,7 @@ const { branches, initial = 'main', rootLabel = 'ROOT', rootSmall = '● clean'
|
||||
padding: 20px;
|
||||
color: var(--paper);
|
||||
background: var(--ink);
|
||||
/* token-gap: source uses 1px solid #41596b over the ink background; no token matches; owner design-system-keeper */
|
||||
border: 1px solid #41596b;
|
||||
border: 1px solid var(--muted);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -177,13 +176,11 @@ const { branches, initial = 'main', rootLabel = 'ROOT', rootSmall = '● clean'
|
||||
}
|
||||
.tree-node small {
|
||||
margin-top: 0;
|
||||
/* token-gap: source uses #9eabb4; no token matches; owner design-system-keeper */
|
||||
color: #9eabb4;
|
||||
color: var(--muted);
|
||||
font-size: var(--step-0);
|
||||
}
|
||||
.tree-node.root small {
|
||||
/* token-gap: source uses #9eabb4 even on the root; no token matches; owner design-system-keeper */
|
||||
color: #9eabb4;
|
||||
color: var(--muted);
|
||||
}
|
||||
.tree-node.active {
|
||||
box-shadow: inset 4px 0 0 var(--gold);
|
||||
|
||||
@@ -99,7 +99,11 @@ const { rootSelector, data } = Astro.props;
|
||||
|
||||
function replace(target, markup) {
|
||||
const panel = root.querySelector(target);
|
||||
if (panel) panel.innerHTML = markup;
|
||||
if (!panel) return;
|
||||
panel.innerHTML = markup;
|
||||
panel.classList.remove('is-swapping');
|
||||
void panel.offsetWidth;
|
||||
panel.classList.add('is-swapping');
|
||||
}
|
||||
|
||||
// Names deliberately match legacy functions until task 19 updates its checks.
|
||||
@@ -291,4 +295,24 @@ const { rootSelector, data } = Astro.props;
|
||||
outline: 3px solid var(--gold);
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
:global(.is-swapping > *) {
|
||||
/* purpose: tab panel change in the guide (state change) */
|
||||
animation: guide-swap 200ms cubic-bezier(0.2, 0, 0, 1) both;
|
||||
}
|
||||
@keyframes guide-swap {
|
||||
from {
|
||||
opacity: 0.15;
|
||||
transform: translateY(5px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.is-swapping > *) {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -42,7 +42,7 @@ const { target = '.reading-progress span' } = Astro.props;
|
||||
|
||||
function update() {
|
||||
const height = document.documentElement.scrollHeight - window.innerHeight;
|
||||
span.style.width = (height > 0 ? (window.scrollY / height) * 100 : 0) + '%';
|
||||
span.style.transform = `scaleX(${height > 0 ? window.scrollY / height : 0})`;
|
||||
}
|
||||
|
||||
// `{ passive: true }` is non-negotiable. The legacy app.js binds
|
||||
|
||||
@@ -403,8 +403,13 @@ fix(api): scope session query</code></pre>
|
||||
select('[data-skill]', id, 'skill');
|
||||
}
|
||||
|
||||
// Keys whose copy carries markup, so the swap must write innerHTML.
|
||||
// `skillsText` was missing while the markup renders it with `set:html`:
|
||||
// the island rewrote it as text on load and every visitor saw a literal
|
||||
// `<code>.agents/skills/</code>` in the paragraph.
|
||||
const HTML_KEYS = {
|
||||
heroTitle: 1,
|
||||
skillsText: 1,
|
||||
pipelineTitle: 1,
|
||||
skillsTitle: 1,
|
||||
ratchetTitle: 1,
|
||||
@@ -487,24 +492,6 @@ fix(api): scope session query</code></pre>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Token-gap escape hatch for legacy values without a token yet — see
|
||||
.agents/scripts/check-tokens.mjs and the rules/astro.md token contract.
|
||||
Owner: design-system-keeper. */
|
||||
/* token-gap: --accent/#7c78a8 etc. already tokenized; --bp-2xl/2200 covers the
|
||||
legacy (min-width:2200px) and (max-width:900px) collapse to --bp-md/800px;
|
||||
owner design-system-keeper */
|
||||
:root {
|
||||
/* token-gap: rules/styles.css values copied verbatim; design-system-keeper */
|
||||
--paper: #f5f4f1;
|
||||
--ink: #172f42; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
--muted: #697b89; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
--accent: #7c78a8; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
--blue: #527f9f; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
--gold: #efc76b; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
--line: #d8dee2; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
--deep: #102536; /* token-gap: rules/styles.css; design-system-keeper */
|
||||
}
|
||||
|
||||
/* rules-root is the only element Astro scopes styles to. The legacy
|
||||
page's rules/styles.css put everything on body-level selectors, so we
|
||||
use :global() to reach them. */
|
||||
@@ -569,7 +556,7 @@ fix(api): scope session query</code></pre>
|
||||
height: 30px;
|
||||
color: var(--paper);
|
||||
background: var(--accent);
|
||||
font-size: 15px; /* token-gap: source 15px; design-system-keeper */
|
||||
font-size: var(--step-1);
|
||||
}
|
||||
.topbar nav {
|
||||
display: flex;
|
||||
@@ -646,7 +633,7 @@ fix(api): scope session query</code></pre>
|
||||
max-width: 750px;
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 16px; /* token-gap: source 16px; design-system-keeper */
|
||||
font-size: var(--step-16);
|
||||
line-height: 1.75;
|
||||
}
|
||||
.hero-foot aside {
|
||||
@@ -666,7 +653,7 @@ fix(api): scope session query</code></pre>
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.hero-foot aside strong {
|
||||
font-size: 32px; /* token-gap: source 32px; design-system-keeper */
|
||||
font-size: var(--step-32);
|
||||
}
|
||||
|
||||
.thesis {
|
||||
@@ -715,7 +702,7 @@ fix(api): scope session query</code></pre>
|
||||
max-width: 650px;
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 14px; /* token-gap: source 14px; design-system-keeper */
|
||||
font-size: var(--step-14);
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
@@ -779,7 +766,7 @@ fix(api): scope session query</code></pre>
|
||||
background: var(--deep);
|
||||
}
|
||||
.stage-number {
|
||||
color: #ffffff24; /* token-gap: stage number 12% white on --deep; design-system-keeper */
|
||||
color: var(--white-14);
|
||||
font:
|
||||
500 76px 'DM Mono',
|
||||
monospace;
|
||||
@@ -801,8 +788,8 @@ fix(api): scope session query</code></pre>
|
||||
.stage-detail p,
|
||||
.skill-detail p {
|
||||
max-width: 650px;
|
||||
color: #b8c8d2; /* token-gap: lighter than --muted on dark bg; design-system-keeper */
|
||||
font-size: 13px; /* token-gap: source 13px; design-system-keeper */
|
||||
color: var(--ink-line);
|
||||
font-size: var(--step-13);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.stage-detail a,
|
||||
@@ -822,7 +809,7 @@ fix(api): scope session query</code></pre>
|
||||
.copy-lab pre {
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
background: #081621; /* token-gap: darker than --deep for pre block; design-system-keeper */
|
||||
background: var(--deep);
|
||||
}
|
||||
.stage-detail pre {
|
||||
align-self: stretch;
|
||||
@@ -832,7 +819,7 @@ fix(api): scope session query</code></pre>
|
||||
.skill-detail code,
|
||||
.example-grid code,
|
||||
.copy-lab code {
|
||||
color: #c9d5dc; /* token-gap: code text on dark bg; design-system-keeper */
|
||||
color: var(--ink-code);
|
||||
font:
|
||||
11px/1.7 'DM Mono',
|
||||
monospace;
|
||||
@@ -870,7 +857,7 @@ fix(api): scope session query</code></pre>
|
||||
}
|
||||
.skill-list button small {
|
||||
color: var(--muted);
|
||||
font-size: 10px; /* token-gap: source 10px; design-system-keeper */
|
||||
font-size: var(--step-00);
|
||||
}
|
||||
.skill-list button.active {
|
||||
color: var(--paper);
|
||||
@@ -878,23 +865,23 @@ fix(api): scope session query</code></pre>
|
||||
box-shadow: inset 6px 0 var(--gold);
|
||||
}
|
||||
.skill-list button.active small {
|
||||
color: #f0eef8; /* token-gap: near-white text on --accent active state; design-system-keeper */
|
||||
color: var(--accent-paper-active);
|
||||
}
|
||||
.skill-detail {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
padding: 50px clamp(30px, 5vw, 80px);
|
||||
color: var(--paper);
|
||||
background: #5b7098; /* token-gap: skill detail panel bg, between --blue and --accent; design-system-keeper */
|
||||
background: var(--accent-surface);
|
||||
}
|
||||
.skill-detail header {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
padding-bottom: 22px;
|
||||
border-bottom: 1px solid #ffffff3a; /* token-gap: 23% white over skill detail; design-system-keeper */
|
||||
border-bottom: 1px solid var(--white-23);
|
||||
}
|
||||
.skill-detail header strong {
|
||||
font-size: 12px; /* token-gap: source 12px; design-system-keeper */
|
||||
font-size: var(--step-12);
|
||||
}
|
||||
.skill-detail pre {
|
||||
margin: 20px 0 0;
|
||||
@@ -959,7 +946,7 @@ fix(api): scope session query</code></pre>
|
||||
}
|
||||
.copy-lab > div p {
|
||||
max-width: 420px;
|
||||
color: #eceaf5; /* token-gap: near-white text on --accent; design-system-keeper */
|
||||
color: var(--accent-paper);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.copy-lab article {
|
||||
@@ -974,7 +961,7 @@ fix(api): scope session query</code></pre>
|
||||
gap: 20px;
|
||||
margin: 20px;
|
||||
padding: 11px 14px;
|
||||
border: 1px solid #ffffff50; /* token-gap: 31% white copy button border; design-system-keeper */
|
||||
border: 1px solid var(--white-31);
|
||||
color: var(--paper);
|
||||
background: transparent;
|
||||
font:
|
||||
@@ -1021,7 +1008,7 @@ fix(api): scope session query</code></pre>
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1px;
|
||||
background: #132b3b; /* token-gap: deeper.nav grid rule color; design-system-keeper */
|
||||
background: var(--ink);
|
||||
}
|
||||
.deeper nav a {
|
||||
display: grid;
|
||||
@@ -1039,10 +1026,10 @@ fix(api): scope session query</code></pre>
|
||||
monospace;
|
||||
}
|
||||
.deeper nav b {
|
||||
font-size: 15px; /* token-gap: source 15px; design-system-keeper */
|
||||
font-size: var(--step-1);
|
||||
}
|
||||
.deeper nav small {
|
||||
color: #9eb0bb; /* token-gap: muted small text on --ink; design-system-keeper */
|
||||
color: var(--ink-muted);
|
||||
font:
|
||||
9px 'DM Mono',
|
||||
monospace;
|
||||
@@ -1080,8 +1067,7 @@ fix(api): scope session query</code></pre>
|
||||
}
|
||||
}
|
||||
|
||||
/* token-gap: legacy breakpoint from rules/styles.css; design-system-keeper */
|
||||
@media (max-width: 900px) {
|
||||
@media (max-width: 800px) {
|
||||
.topbar {
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
@@ -1126,8 +1112,7 @@ fix(api): scope session query</code></pre>
|
||||
}
|
||||
}
|
||||
|
||||
/* token-gap: legacy breakpoint from rules/styles.css; design-system-keeper */
|
||||
@media (max-width: 600px) {
|
||||
@media (max-width: 560px) {
|
||||
main {
|
||||
padding-inline: 18px;
|
||||
}
|
||||
@@ -1171,7 +1156,7 @@ fix(api): scope session query</code></pre>
|
||||
padding: 28px 22px;
|
||||
}
|
||||
.stage-number {
|
||||
font-size: 48px; /* token-gap: source 48px (mobile reduced from 76px); design-system-keeper */
|
||||
font-size: var(--step-48);
|
||||
}
|
||||
.stage-detail pre {
|
||||
grid-column: auto;
|
||||
|
||||
@@ -140,8 +140,7 @@ const packageFiles: PackageFile[] = [
|
||||
|
||||
.package-tree {
|
||||
padding: 22px 16px;
|
||||
/* token-gap: legacy 1px solid #426070 over --ink; no token matches; owner design-system-keeper */
|
||||
border-right: 1px solid #426070;
|
||||
border-right: 1px solid var(--muted);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -149,9 +148,8 @@ const packageFiles: PackageFile[] = [
|
||||
.package-preview > span {
|
||||
margin: 0 0 14px;
|
||||
color: var(--gold);
|
||||
/* token-gap: source uses 11px monospace; no --step-* covers 11px on this surface; design-system-keeper */
|
||||
font:
|
||||
700 11px / 1.35 ui-monospace,
|
||||
700 var(--step-0) / 1.35 ui-monospace,
|
||||
monospace;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
@@ -166,8 +164,7 @@ const packageFiles: PackageFile[] = [
|
||||
border: 0;
|
||||
border-left: 2px solid transparent;
|
||||
background: transparent;
|
||||
/* token-gap: legacy #d6e1e4 over --ink tree buttons; no token matches; design-system-keeper */
|
||||
color: #d6e1e4;
|
||||
color: var(--line);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
@@ -180,8 +177,7 @@ const packageFiles: PackageFile[] = [
|
||||
.package-tree button:focus-visible,
|
||||
.package-tree button.active {
|
||||
border-left-color: var(--gold);
|
||||
/* token-gap: legacy #1f3a4b active/hover on --ink tree; no token matches; design-system-keeper */
|
||||
background: #1f3a4b;
|
||||
background: var(--blue);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
@@ -198,8 +194,7 @@ const packageFiles: PackageFile[] = [
|
||||
}
|
||||
|
||||
.package-tree small {
|
||||
/* token-gap: legacy #aebfc7 muted text on --ink tree; no token matches; design-system-keeper */
|
||||
color: #aebfc7;
|
||||
color: var(--muted);
|
||||
font:
|
||||
11px / 1.25 Arial,
|
||||
sans-serif;
|
||||
@@ -209,8 +204,7 @@ const packageFiles: PackageFile[] = [
|
||||
.package-preview {
|
||||
min-width: 0;
|
||||
padding: 26px;
|
||||
/* token-gap: legacy #173245 preview surface (between --ink and --blue); no token matches; design-system-keeper */
|
||||
background: #173245;
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
@@ -224,8 +218,7 @@ const packageFiles: PackageFile[] = [
|
||||
.package-preview p {
|
||||
max-width: 52ch;
|
||||
margin: 0;
|
||||
/* token-gap: legacy #d6e1e4 muted text on #173245 preview; no token matches; design-system-keeper */
|
||||
color: #d6e1e4;
|
||||
color: var(--line);
|
||||
}
|
||||
|
||||
.package-preview pre {
|
||||
@@ -233,19 +226,17 @@ const packageFiles: PackageFile[] = [
|
||||
margin: 20px 0 0;
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
/* token-gap: legacy #466274 rule on preview pre border; no token matches; design-system-keeper */
|
||||
border: 1px solid #466274;
|
||||
/* token-gap: legacy #102837 fill on preview pre background; no token matches; design-system-keeper */
|
||||
background: #102837;
|
||||
/* token-gap: legacy #d6e1e4 code text on #102837; no token matches; design-system-keeper */
|
||||
color: #d6e1e4;
|
||||
border: 1px solid var(--muted);
|
||||
background: var(--deep);
|
||||
color: var(--line);
|
||||
font:
|
||||
12px / 1.55 ui-monospace,
|
||||
monospace;
|
||||
}
|
||||
|
||||
.package-preview.is-swapping {
|
||||
animation: package-preview-in 0.34s ease both;
|
||||
/* purpose: review-desk detail swap on selection (state change) */
|
||||
animation: package-preview-in 200ms cubic-bezier(0.2, 0, 0, 1) both;
|
||||
}
|
||||
|
||||
@keyframes package-preview-in {
|
||||
@@ -265,16 +256,15 @@ const packageFiles: PackageFile[] = [
|
||||
}
|
||||
.package-tree {
|
||||
border-right: 0;
|
||||
/* token-gap: legacy #426070 mobile layout rule over --ink; no token matches; design-system-keeper */
|
||||
border-bottom: 1px solid #426070;
|
||||
/* token-gap: legacy var(--muted) mobile layout rule over --ink; no token matches; design-system-keeper */
|
||||
border-bottom: 1px solid var(--muted);
|
||||
}
|
||||
.package-preview {
|
||||
padding: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
/* token-gap: legacy 520px breakpoint from skills/styles.css (mobile phone), not in named set 560/800/1100/1600/2200; design-system-keeper */
|
||||
@media (max-width: 520px) {
|
||||
@media (max-width: 560px) {
|
||||
.package-tree {
|
||||
padding: 18px 10px;
|
||||
}
|
||||
@@ -288,8 +278,7 @@ const packageFiles: PackageFile[] = [
|
||||
padding: 18px;
|
||||
}
|
||||
.package-preview pre {
|
||||
/* token-gap: legacy 11px mobile font-size from skills/styles.css; no --step-* covers 11px; design-system-keeper */
|
||||
font-size: 11px;
|
||||
font-size: var(--step-0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,7 @@
|
||||
"en": "Drop the style for security warnings, irreversible actions, and sequences where terse wording can be misread.",
|
||||
"pt": "Abandone o estilo em alertas de segurança, ações irreversíveis e sequências onde concisão pode causar erro."
|
||||
},
|
||||
"source": "https://github.com/JuliusBrussee/caveman/blob/3b74643f4d910f496babd4e634b1ba7168816f14/skills/caveman/SKILL.md"
|
||||
"source": "https://github.com/JuliusBrussee/caveman/blob/3b74643f4d910f496babd4e634b1ba7168816f14/skills/caveman/SKILL.md",
|
||||
"label": { "en": "COMMUNICATE", "pt": "COMUNICAR" },
|
||||
"tagline": { "en": "signal without filler", "pt": "sinal sem excesso" }
|
||||
}
|
||||
|
||||
@@ -19,5 +19,7 @@
|
||||
"en": "Do not jump from error message straight to a patch.",
|
||||
"pt": "Não pule da mensagem de erro direto para um patch."
|
||||
},
|
||||
"source": "https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/diagnosing-bugs/SKILL.md"
|
||||
"source": "https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/diagnosing-bugs/SKILL.md",
|
||||
"label": { "en": "DIAGNOSE", "pt": "DIAGNOSTICAR" },
|
||||
"tagline": { "en": "tight feedback loop", "pt": "ciclo curto de feedback" }
|
||||
}
|
||||
|
||||
@@ -19,5 +19,7 @@
|
||||
"en": "Never simplify away security, accessibility, validation, or real edge cases.",
|
||||
"pt": "Nunca simplifique segurança, acessibilidade, validação ou casos extremos reais."
|
||||
},
|
||||
"source": "https://github.com/ilindaniel/ponytail-lite/blob/e7b42dc2d384a702240dea4d52a7bf5530b821b6/AGENTS.md"
|
||||
"source": "https://github.com/ilindaniel/ponytail-lite/blob/e7b42dc2d384a702240dea4d52a7bf5530b821b6/AGENTS.md",
|
||||
"label": { "en": "SIMPLIFY", "pt": "SIMPLIFICAR" },
|
||||
"tagline": { "en": "minimum code that holds", "pt": "código mínimo que funciona" }
|
||||
}
|
||||
|
||||
@@ -16,5 +16,7 @@
|
||||
"en": "Practitioner articles add context; they do not override official behavior.",
|
||||
"pt": "Artigos de praticantes dão contexto; não substituem comportamento oficial."
|
||||
},
|
||||
"source": "https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/research/SKILL.md"
|
||||
"source": "https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/research/SKILL.md",
|
||||
"label": { "en": "INVESTIGATE", "pt": "INVESTIGAR" },
|
||||
"tagline": { "en": "primary sources first", "pt": "fontes primárias primeiro" }
|
||||
}
|
||||
|
||||
@@ -19,5 +19,7 @@
|
||||
"en": "Pin the comparison point and source specification before reviewing.",
|
||||
"pt": "Fixe o ponto de comparação e a especificação antes de revisar."
|
||||
},
|
||||
"source": "https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/code-review/SKILL.md"
|
||||
"source": "https://github.com/mattpocock/skills/blob/6654f6b60cd9d5be8b54c6fafe44346dabeb3b76/skills/engineering/code-review/SKILL.md",
|
||||
"label": { "en": "REVIEW", "pt": "REVISAR" },
|
||||
"tagline": { "en": "standards × spec", "pt": "padrões × especificação" }
|
||||
}
|
||||
|
||||
@@ -19,5 +19,7 @@
|
||||
"en": "Read raw output when exact wording or full diffs matter.",
|
||||
"pt": "Leia saída bruta quando texto exato ou diffs completos importarem."
|
||||
},
|
||||
"source": "https://github.com/aetox-skills/token-saver/blob/8f21188bb043fad411f47e2e57f0365a83c13da7/SKILL.md"
|
||||
"source": "https://github.com/aetox-skills/token-saver/blob/8f21188bb043fad411f47e2e57f0365a83c13da7/SKILL.md",
|
||||
"label": { "en": "ECONOMIZE", "pt": "ECONOMIZAR" },
|
||||
"tagline": { "en": "compress noisy output", "pt": "comprima saídas ruidosas" }
|
||||
}
|
||||
|
||||
@@ -19,5 +19,7 @@
|
||||
"en": "Too heavy for trivial edits or factual answers.",
|
||||
"pt": "Pesado demais para edições triviais ou respostas factuais."
|
||||
},
|
||||
"source": "https://github.com/Leonxlnx/unlazy/blob/473d4b80421c36d733042434cd4b938f81a19ef1/SKILL.md"
|
||||
"source": "https://github.com/Leonxlnx/unlazy/blob/473d4b80421c36d733042434cd4b938f81a19ef1/SKILL.md",
|
||||
"label": { "en": "COMPLETE", "pt": "CONCLUIR" },
|
||||
"tagline": { "en": "gates and evidence", "pt": "gates e evidências" }
|
||||
}
|
||||
|
||||
@@ -235,12 +235,22 @@ const skillWorkflow = defineCollection({
|
||||
// render time; here it is embedded to match the GuideSelectorData
|
||||
// contract. If the URL changes, both this entry and the matching
|
||||
// `skillSources/<id>.json` must be updated.
|
||||
//
|
||||
// `label` and `tagline` are the selector *button*'s two lines and are
|
||||
// distinct from `kind` and `use`, which belong to the detail panel:
|
||||
// ponytail's button reads "SIMPLIFY" / "minimum code that holds" while its
|
||||
// panel reads "SIMPLIFICATION INSTINCT" and a paragraph of prose. They are
|
||||
// not in `interactiveCopy` — the English lives in the `full-guide/index.html`
|
||||
// markup and the Portuguese in `translations.pt` — so task 05b had nothing
|
||||
// to migrate them from and the page rendered `kind`/`use` on the buttons.
|
||||
const commonSkills = defineCollection({
|
||||
type: 'data',
|
||||
schema: z.object({
|
||||
id: z.string(),
|
||||
number: z.string(),
|
||||
kind: localized,
|
||||
label: localized,
|
||||
tagline: localized,
|
||||
title: z.string(),
|
||||
rule: localized,
|
||||
use: localized,
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
"skillReadSkill": "READ SKILL ↗",
|
||||
"skillReadSkillPt": "LER SKILL ↗",
|
||||
"skillTriggerLabel": "TRIGGER",
|
||||
"skillTriggerLabelPt": "GATILHO",
|
||||
"copyStatus": "Prompt copied.",
|
||||
"copyStatusPt": "Prompt copiado.",
|
||||
"copyFallback": "Select the text manually.",
|
||||
@@ -118,8 +117,7 @@
|
||||
"stageOpenSourcePt": "ABRIR FONTE ↗",
|
||||
"skillReadSkill": "LER SKILL ↗",
|
||||
"skillReadSkillPt": "LER SKILL ↗",
|
||||
"skillTriggerLabel": "TRIGGER",
|
||||
"skillTriggerLabelPt": "GATILHO",
|
||||
"skillTriggerLabel": "GATILHO",
|
||||
"copyStatus": "Prompt copied.",
|
||||
"copyStatusPt": "Prompt copiado.",
|
||||
"copyFallback": "Select the text manually.",
|
||||
|
||||
@@ -874,17 +874,19 @@ const base = import.meta.env.BASE_URL;
|
||||
aria-selected={skill.id === 'ponytail'}
|
||||
>
|
||||
<>
|
||||
{/* The button's two lines are `label` / `tagline`, not the
|
||||
panel's `kind` / `use`. See src/content/config.ts. */}
|
||||
<span>
|
||||
<span data-language-content="en">{skill.kind.en}</span>
|
||||
<span data-language-content="en">{skill.label.en}</span>
|
||||
<span data-language-content="pt" hidden>
|
||||
{skill.kind.pt}
|
||||
{skill.label.pt}
|
||||
</span>
|
||||
</span>
|
||||
<strong>{skill.title}</strong>
|
||||
<small>
|
||||
<span data-language-content="en">{skill.use.en}</span>
|
||||
<span data-language-content="en">{skill.tagline.en}</span>
|
||||
<span data-language-content="pt" hidden>
|
||||
{skill.use.pt}
|
||||
{skill.tagline.pt}
|
||||
</span>
|
||||
</small>
|
||||
</>
|
||||
@@ -1062,6 +1064,22 @@ const base = import.meta.env.BASE_URL;
|
||||
>
|
||||
</p>
|
||||
</section>
|
||||
<!-- Legacy full-guide/index.html closes with this section after `.sources`.
|
||||
Task 15d dropped it, and nothing caught that: verify.mjs reads the
|
||||
legacy file, so its chapter-route assertion kept passing. It has no
|
||||
`translations.pt` entry, so it is English-only on the live site too. -->
|
||||
<section class="chapter-route">
|
||||
<div class="section-label">
|
||||
<span>Navigate by idea</span><span>short chapters / one system</span>
|
||||
</div>
|
||||
<p>
|
||||
Prefer a focused chapter? Start with the <a href={`${base}summary/`}>route map</a>, then
|
||||
jump directly to <a href={`${base}models/`}>models</a>, <a href={`${base}agents/`}
|
||||
>agents and worktrees</a
|
||||
>, <a href={`${base}skills/`}>skill creation</a>, <a href={`${base}rules/`}>rules</a>, or
|
||||
the <a href={`${base}skills-review/`}>skills review desk</a>.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
<GuideSelector rootSelector="#full-guide" data={selectorData} />
|
||||
</BaseLayout>
|
||||
|
||||
+5
-14
@@ -84,10 +84,8 @@ const cards = data.cards ?? [];
|
||||
padding: 12px 16px;
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
/* token-gap: legacy 12px fixed from landing.css; --step-0 is 11px;
|
||||
no token matches 12px; owner design-system-keeper */
|
||||
font:
|
||||
700 12px ui-monospace,
|
||||
700 var(--step-12) ui-monospace,
|
||||
monospace;
|
||||
letter-spacing: 0.06em;
|
||||
text-decoration: none;
|
||||
@@ -104,8 +102,7 @@ const cards = data.cards ?? [];
|
||||
|
||||
.guide-launch span {
|
||||
color: var(--gold);
|
||||
/* token-gap: legacy 22px arrow from landing.css; no --step-* covers 22px; owner design-system-keeper */
|
||||
font-size: 22px;
|
||||
font-size: var(--step-22);
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
@@ -121,25 +118,19 @@ const cards = data.cards ?? [];
|
||||
|
||||
.landing-note span {
|
||||
color: var(--red);
|
||||
/* token-gap: legacy 11px monospace eyebrow (no DM Mono in stack);
|
||||
--step-0 also resolves to 11px but with different font weight;
|
||||
preserved verbatim from landing.css; owner design-system-keeper */
|
||||
font:
|
||||
700 11px ui-monospace,
|
||||
700 var(--step-0) ui-monospace,
|
||||
monospace;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
.landing-note strong {
|
||||
/* token-gap: clamp(22px,3.2vw,42px) is specific to this strip; no
|
||||
--step-* ramp covers both endpoints; owner design-system-keeper */
|
||||
font-size: clamp(22px, 3.2vw, 42px);
|
||||
font-size: var(--step-4);
|
||||
line-height: 1.08;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
/* token-gap: 520px collapse from landing.css (was bottom-bar threshold); owner design-system-keeper */
|
||||
@media (max-width: 520px) {
|
||||
@media (max-width: 560px) {
|
||||
.landing-note {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 9px;
|
||||
|
||||
@@ -20,8 +20,43 @@
|
||||
--step-6: clamp(36px, 5vw, 65px);
|
||||
--step-5: clamp(24px, 3vw, 38px);
|
||||
--step-4: clamp(22px, 3vw, 36px);
|
||||
--step-48: 48px;
|
||||
--step-32: 32px;
|
||||
--step-30: 30px;
|
||||
--step-25: 25px;
|
||||
--step-24: 24px;
|
||||
--step-22: 22px;
|
||||
--step-20: 20px;
|
||||
--step-18: 18px;
|
||||
--step-17: 17px;
|
||||
--step-16: 16px;
|
||||
--step-1: 15px;
|
||||
--step-14: 14px;
|
||||
--step-13: 13px;
|
||||
--step-12: 12px;
|
||||
--step-0: 11px;
|
||||
--step-00: 10px;
|
||||
--step-09: 9px;
|
||||
|
||||
/* On-dark palette. These are the values the source actually uses on the
|
||||
--ink and --accent surfaces. They are not near-misses of the palette
|
||||
above: --muted is a dark-on-light value and is illegible on --ink, and
|
||||
--paper is warm where the text over --accent is violet-tinted. Each one
|
||||
is the exact hex the legacy stylesheets ship. */
|
||||
--ink-muted: #9eb0bb;
|
||||
--ink-line: #b8c8d2;
|
||||
--ink-code: #c9d5dc;
|
||||
--accent-paper: #eceaf5;
|
||||
--accent-paper-active: #f0eef8;
|
||||
--accent-surface: #5b7098;
|
||||
|
||||
/* Overlays. The percentages are the exact 8-bit alphas the source ships
|
||||
(#ffffff24, #ffffff3a, #ffffff40, #ffffff50), not rounded to whole
|
||||
numbers — the names are rounded, the values are not. */
|
||||
--white-14: rgb(255 255 255 / 14.1176%);
|
||||
--white-23: rgb(255 255 255 / 22.7451%);
|
||||
--white-25: rgb(255 255 255 / 25.098%);
|
||||
--white-31: rgb(255 255 255 / 31.3725%);
|
||||
|
||||
/* Breakpoints */
|
||||
--bp-sm: 560px;
|
||||
|
||||
+1
-70
@@ -1,70 +1 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>AI For Dummies — Route map</title>
|
||||
<link rel="stylesheet" href="../chapters.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header class="top">
|
||||
<a href="../full-guide/">← AI FOR DUMMIES</a><span>00 / ROUTE MAP</span
|
||||
><a href="../skills-review/">review desk ↗</a>
|
||||
</header>
|
||||
<section class="hero">
|
||||
<p class="eyebrow">Start here</p>
|
||||
<h1>Ship the<br /><em>system.</em></h1>
|
||||
<p>
|
||||
This guide turns AI work into a shape: frame the problem, choose the model and agent,
|
||||
isolate changes, teach repeatable decisions, and verify the result.
|
||||
</p>
|
||||
</section>
|
||||
<section class="grid">
|
||||
<article class="card">
|
||||
<b>01</b>
|
||||
<h2>Models</h2>
|
||||
<p>Capability and effort are separate knobs.</p>
|
||||
<a href="../models/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>02</b>
|
||||
<h2>Agents & trees</h2>
|
||||
<p>Bound roles, handoffs, and worktrees.</p>
|
||||
<a href="../agents/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>03</b>
|
||||
<h2>Skills</h2>
|
||||
<p>Capture repeatable decisions.</p>
|
||||
<a href="../skills/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>04</b>
|
||||
<h2>Rules</h2>
|
||||
<p>Connect guidance to enforcement.</p>
|
||||
<a href="../rules/">Open chapter →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>05</b>
|
||||
<h2>Practice</h2>
|
||||
<p>Compare prompts and skill-enabled runs.</p>
|
||||
<a href="../hands-on/starter/">Open lab →</a>
|
||||
</article>
|
||||
<article class="card">
|
||||
<b>06</b>
|
||||
<h2>Review desk</h2>
|
||||
<p>Browse original files and improved drafts.</p>
|
||||
<a href="../skills-review/">Open desk →</a>
|
||||
</article>
|
||||
</section>
|
||||
<nav class="links">
|
||||
<a href="../full-guide/">Full field guide</a
|
||||
><a href="../docs/operations-guide.md">Operations guide</a>
|
||||
</nav>
|
||||
<footer>
|
||||
Each chapter stands alone; the order follows a real task becoming a reliable change.
|
||||
</footer>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>AI For Dummies — Route map</title><link rel="stylesheet" href="../chapters.css"></head><body><main><header class="top"><a href="../full-guide/">← AI FOR DUMMIES</a><span>00 / ROUTE MAP</span><a href="../skills-review/">review desk ↗</a></header><section class="hero"><p class="eyebrow">Start here</p><h1>Ship the<br><em>system.</em></h1><p>This guide turns AI work into a shape: frame the problem, choose the model and agent, isolate changes, teach repeatable decisions, and verify the result.</p></section><section class="grid"><article class="card"><b>01</b><h2>Models</h2><p>Capability and effort are separate knobs.</p><a href="../models/">Open chapter →</a></article><article class="card"><b>02</b><h2>Agents & trees</h2><p>Bound roles, handoffs, and worktrees.</p><a href="../agents/">Open chapter →</a></article><article class="card"><b>03</b><h2>Skills</h2><p>Capture repeatable decisions.</p><a href="../skills/">Open chapter →</a></article><article class="card"><b>04</b><h2>Rules</h2><p>Connect guidance to enforcement.</p><a href="../rules/">Open chapter →</a></article><article class="card"><b>05</b><h2>Practice</h2><p>Compare prompts and skill-enabled runs.</p><a href="../hands-on/starter/">Open lab →</a></article><article class="card"><b>06</b><h2>Review desk</h2><p>Browse original files and improved drafts.</p><a href="../skills-review/">Open desk →</a></article></section><nav class="links"><a href="../full-guide/">Full field guide</a><a href="../docs/operations-guide.md">Operations guide</a></nav><footer>Each chapter stands alone; the order follows a real task becoming a reliable change.</footer></main></body></html>
|
||||
|
||||
Reference in New Issue
Block a user