Compare commits
39 Commits
af1133f019
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9015e7bd1d | |||
| d88d8b89eb | |||
| b119b92948 | |||
| 580293867d | |||
| 9cb1e0d242 | |||
| 12c32d2fd7 | |||
| 5c76a13b4d | |||
| 91e8d380d0 | |||
| 0a12e9cbcc | |||
| e3ce8abe89 | |||
| 138d7c5e4e | |||
| 33ce544a1e | |||
| f53159f0b4 | |||
| 0c02780dd9 | |||
| aa28158534 | |||
| 429b4e2e87 | |||
| c9796a3e7f | |||
| 9486188983 | |||
| 2560437bfa | |||
| bf46363ea9 | |||
| 18862e8e13 | |||
| 6974800d2e | |||
| bd3805f883 | |||
| 8f82304758 | |||
| a97c2a4034 | |||
| a1eb1e79ea | |||
| c2b35d5355 | |||
| 8c7ca2aded | |||
| c7d06a120d | |||
| aefbd206f9 | |||
| 0bca43cbfd | |||
| 47fff7d289 | |||
| 2b621843a2 | |||
| d15c301fdb | |||
| 9ca3f48def | |||
| 12bc1dc067 | |||
| 71c74fb7f1 | |||
| edd03f9716 | |||
| 6bfae2033e |
@@ -1,45 +1,47 @@
|
|||||||
# Context: architecture, current and target
|
# Context: architecture
|
||||||
|
|
||||||
## Current (no build step)
|
## Current (Astro, static output)
|
||||||
|
|
||||||
Ten hand-written HTML pages, each linking its own CSS and one ES module:
|
Ten routes, one `src/pages/` entry each, built to `dist/`:
|
||||||
|
|
||||||
| Route | Page | Script | Stylesheets |
|
| Route | Page | Islands |
|
||||||
| -------------------- | -------------------------- | ---------------------- | --------------------------------------------- |
|
| -------------------- | ------------------------------- | ----------------------------------------------- |
|
||||||
| `/` | `index.html` | — | `chapters.css`, `landing.css` |
|
| `/` | `src/pages/index.astro` | — |
|
||||||
| `/full-guide/` | `full-guide/index.html` | `app.js` (50 KB) | `styles.css`, `responsive.css`, `audit.css` |
|
| `/full-guide/` | `src/pages/full-guide.astro` | `GuideSelector`, `LanguageToggle`, `CopyPrompt` |
|
||||||
| `/summary/` | `summary/index.html` | — | `chapters.css` |
|
| `/summary/` | `src/pages/summary.astro` | — |
|
||||||
| `/models/` | `models/index.html` | — | `chapters.css` |
|
| `/models/` | `src/pages/models.astro` | — |
|
||||||
| `/agents/` | `agents/index.html` | — | `chapters.css` |
|
| `/agents/` | `src/pages/agents.astro` | — |
|
||||||
| `/skills/` | `skills/index.html` | `skills/app.js` | `skills/styles.css` |
|
| `/skills/` | `src/pages/skills.astro` | `SkillPackageExplorer` |
|
||||||
| `/rules/` | `rules/index.html` | `rules/app.js` | `rules/styles.css` |
|
| `/rules/` | `src/pages/rules.astro` | `RulesInteractive` |
|
||||||
| `/skills-review/` | `skills-review/index.html` | `skills-review/app.js` | `skills-review/styles.css`, `change-lens.css` |
|
| `/skills-review/` | `src/pages/skills-review.astro` | `legacy/skills-review/app.js` |
|
||||||
| `/hands-on/starter/` | lab fixture | own | own |
|
| `/hands-on/starter/` | `public/` lab fixture | own |
|
||||||
| `/hands-on/rules/` | lab fixture | own | own |
|
| `/hands-on/rules/` | `public/` lab fixture | own |
|
||||||
|
|
||||||
Weight is concentrated: `app.js` 50 KB, `responsive.css` 30 KB,
|
## What is still unmigrated
|
||||||
`skills-review/catalog.js` 27 KB, `skills-review/submitted-catalog.js` 18 KB.
|
|
||||||
|
|
||||||
### What each big file actually is
|
`legacy/` holds the parts the migration did not componentize. They are not dead
|
||||||
|
files — the pages listed above import them, and the build fails without them.
|
||||||
|
|
||||||
- **`app.js`** — not really application code. It is a **bilingual content
|
- **`legacy/styles/guide.css`** (was `styles.css`) — the editorial visual
|
||||||
database** (`phases`, `handsOnPrompts`, `modelGuide`, `skillSources`,
|
system, imported by `full-guide.astro`.
|
||||||
`skillInstallPrompts`, each keyed `{en, pt}`) plus ~12 small `render*`
|
- **`legacy/styles/audit.css`** (was `full-guide/audit.css`) — responsive audit
|
||||||
functions that swap `innerHTML` on tab clicks. ~50 `en:` keys. The content
|
overrides, imported by `full-guide.astro`.
|
||||||
should become data; only the tab behaviour is interactive.
|
- **`legacy/styles/chapters.css`** — imported by `ChapterLayout.astro`.
|
||||||
- **`responsive.css`** — a 30 KB append-only layer of overrides bolted on top of
|
- **`legacy/styles/skills.css`**, **`skills-review.css`**, **`change-lens.css`**
|
||||||
`styles.css`. Expect large parts to be dead once layout moves into components.
|
— imported by their respective pages.
|
||||||
Do not port it verbatim.
|
- **`legacy/skills-review/`** — `app.js` and the module graph under it
|
||||||
- **`skills-review/catalog.js`** — the real data model of the review desk: one
|
(`catalog.js`, `submitted-catalog.js`, `files.js`, `submitted-files.js`,
|
||||||
entry per submitted skill with `id`, `author`, `title`, `status`, `focus`,
|
`vote.js`). `catalog.js` + `submitted-catalog.js` are the review desk's real
|
||||||
`wins[]`, `improve[]`, `extras`, `improved` (full markdown). 24 entries across
|
data model, 24 entries; they are a content collection in all but name.
|
||||||
`catalog.js` + `submitted-catalog.js`. This is already a content collection in
|
|
||||||
all but name.
|
|
||||||
- **`skills-review/files.js` / `submitted-files.js`** — generated file
|
|
||||||
manifests.
|
|
||||||
- **`vote.js`** — the vote widget island; talks to `vote-service/`.
|
|
||||||
|
|
||||||
## Target (Astro)
|
These sit outside `src/` deliberately: `check-tokens.mjs` sweeps `src`, and
|
||||||
|
these files are full of raw hex and unnamed breakpoints. Moving one into `src/`
|
||||||
|
means migrating it to tokens in the same change, not adding an exclusion.
|
||||||
|
|
||||||
|
`responsive.css`, `landing.css`, `app.js`, `rules/app.js`, `rules/styles.css`,
|
||||||
|
and `skills/app.js` were deleted at cutover: their content lives in components.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
@@ -52,15 +54,15 @@ public/
|
|||||||
hands-on/ lab fixtures copied verbatim, never processed
|
hands-on/ lab fixtures copied verbatim, never processed
|
||||||
```
|
```
|
||||||
|
|
||||||
### Non-negotiables for the target
|
### Non-negotiables
|
||||||
|
|
||||||
- **URLs do not change.** `/full-guide/`, `/skills-review/`,
|
- **URLs do not change.** `/full-guide/`, `/skills-review/`,
|
||||||
`/hands-on/starter/` and the rest must resolve exactly as they do now,
|
`/hands-on/starter/` and the rest must resolve exactly as they do now,
|
||||||
trailing slash included. Existing links (including `docs/`, SilverBullet, and
|
trailing slash included. Existing links (including `docs/`, SilverBullet, and
|
||||||
shared URLs with `?author=…&skill=…&view=…` query params) must keep working.
|
shared URLs with `?author=…&skill=…&view=…` query params) must keep working.
|
||||||
- **Zero JS by default.** Seven of the ten pages ship no JavaScript today. They
|
- **Zero JS by default.** Seven of the ten pages ship no JavaScript. They must
|
||||||
must still ship none. Islands are opt-in, per component, and justified.
|
still ship none. Islands are opt-in, per component, and justified.
|
||||||
- **`hands-on/` stays vanilla.** It goes in `public/` untouched. It is a lab
|
- **`hands-on/` stays vanilla.** It lives in `public/` untouched. It is a lab
|
||||||
fixture, not a component.
|
fixture, not a component.
|
||||||
- **No external runtime requests.** `audit-ui.mjs` enforces this and it is part
|
- **No external runtime requests.** `audit-ui.mjs` enforces this and it is part
|
||||||
of the site's thesis. Self-host anything you add.
|
of the site's thesis. Self-host anything you add.
|
||||||
@@ -70,7 +72,12 @@ public/
|
|||||||
|
|
||||||
## Companion service
|
## Companion service
|
||||||
|
|
||||||
`vote-service/` is a Go API on its own Kubernetes deploy cycle, reached by the
|
The vote API is a Go service on its own Kubernetes deploy cycle, reached by the
|
||||||
review desk over `window.SKILLS_REVIEW_VOTE_API`. The refactor does not touch
|
review desk over `window.SKILLS_REVIEW_VOTE_API`. Its source left this
|
||||||
it. Keep the global, or replace it with a build-time `PUBLIC_VOTE_API` env var —
|
repository on 2026-09-06; the deployed service is unchanged, and the review desk
|
||||||
but if you do, update `vote-service/README.md` in the same change.
|
still calls it. Keep the global, or replace it with a build-time
|
||||||
|
`PUBLIC_VOTE_API` env var — but if you do, update the service's own README in
|
||||||
|
the same change.
|
||||||
|
|
||||||
|
Its one-vote-per-IP assertion left `verify.mjs` with it. See
|
||||||
|
[`assertion-removals.md`](assertion-removals.md).
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Assertion removal ledger
|
||||||
|
|
||||||
|
`scripts/verify.mjs` may only lose an assertion by adding an entry here. The
|
||||||
|
gate counts the `## ` headings in this file and allows exactly that many
|
||||||
|
removals below the recorded floor — so a reduction is impossible without a
|
||||||
|
written reason landing in the same commit, as a visible diff.
|
||||||
|
|
||||||
|
Adding an entry is not a formality. An assertion pins a real contract; removing
|
||||||
|
one means that contract is now unverified. Say where it moved, or say plainly
|
||||||
|
that nothing checks it any more.
|
||||||
|
|
||||||
|
## vote-service one-vote-per-IP contract
|
||||||
|
|
||||||
|
**Removed:** 2026-09-06, when `vote-service/` was taken out of this repository.
|
||||||
|
|
||||||
|
**What it asserted:** that `vote-service/main.go` contained both
|
||||||
|
`X-Forwarded-For` and `one active vote per skill` — the review desk's only
|
||||||
|
anti-abuse control, one vote per visitor enforced server-side by source IP.
|
||||||
|
|
||||||
|
**Why it went:** there is no file left to read. The check was a substring match
|
||||||
|
against source that now lives elsewhere.
|
||||||
|
|
||||||
|
**Where it must be re-asserted:** in whichever repository holds the service. The
|
||||||
|
deployed service still enforces the contract; nothing in this repository proves
|
||||||
|
it. If `vote-service/` ever comes back here, restore the assertion and delete
|
||||||
|
this entry.
|
||||||
@@ -19,7 +19,7 @@ Only these need interactivity. Anything else claiming island status is wrong:
|
|||||||
| --------------------------------- | ---------------------------------- | ---------------- |
|
| --------------------------------- | ---------------------------------- | ---------------- |
|
||||||
| Guide phase/tab switchers | click-driven panel swap | `client:visible` |
|
| Guide phase/tab switchers | click-driven panel swap | `client:visible` |
|
||||||
| Review desk catalog + file viewer | search, filter, fetch source files | `client:load` |
|
| Review desk catalog + file viewer | search, filter, fetch source files | `client:load` |
|
||||||
| Vote widget | talks to `vote-service/` | `client:visible` |
|
| Vote widget | talks to the vote API | `client:visible` |
|
||||||
| Language toggle | swaps EN/PT across the page | `client:idle` |
|
| Language toggle | swaps EN/PT across the page | `client:idle` |
|
||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// Compare the *computed* styles of a legacy page against its Astro
|
||||||
|
// replacement, at several viewport widths.
|
||||||
|
//
|
||||||
|
// node .agents/scripts/computed-style-diff.mjs full-guide
|
||||||
|
// node .agents/scripts/computed-style-diff.mjs full-guide --widths 560,880,1050
|
||||||
|
//
|
||||||
|
// Why this exists: a ported media query can sit in the built stylesheet,
|
||||||
|
// match the viewport, and still do nothing. Astro scopes a component's rules
|
||||||
|
// as `.tree-node[data-astro-cid-lsutp3lb]` (specificity 0,2,0); a rule ported
|
||||||
|
// verbatim as `.tree-node` (0,1,0) loses to it and never applies. Task 15e
|
||||||
|
// attempt 4 shipped exactly that: `@media (max-width: 1050px) .tree-node
|
||||||
|
// { width: 145px }` was present in dist and the node stayed 180px wide.
|
||||||
|
//
|
||||||
|
// Checking that the breakpoint *appears* in the built CSS cannot catch this.
|
||||||
|
// Only asking the browser what it actually computed can.
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import { cpSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
||||||
|
import { createServer } from 'node:net';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { chromium } from 'playwright';
|
||||||
|
|
||||||
|
const route = process.argv[2];
|
||||||
|
if (!route) {
|
||||||
|
console.error('usage: computed-style-diff.mjs <route> [--widths a,b,c]');
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const widthsArg = process.argv.indexOf('--widths');
|
||||||
|
const widths =
|
||||||
|
widthsArg === -1
|
||||||
|
? [520, 560, 600, 620, 720, 800, 880, 1050, 1100, 1600]
|
||||||
|
: process.argv[widthsArg + 1].split(',').map(Number);
|
||||||
|
|
||||||
|
// The selectors worth checking are the ones the responsive layer moves at a
|
||||||
|
// breakpoint, so read them out of the legacy stylesheet's @media blocks only.
|
||||||
|
// Taking every class in the file buries the signal under generic ones like
|
||||||
|
// `.active`, whose state the islands own anyway.
|
||||||
|
const responsive = readFileSync(new URL('../../responsive.css', import.meta.url), 'utf8');
|
||||||
|
const mediaBlocks = [];
|
||||||
|
for (const match of responsive.matchAll(/@media[^{]*\{/g)) {
|
||||||
|
let depth = 0;
|
||||||
|
for (let i = match.index; i < responsive.length; i += 1) {
|
||||||
|
if (responsive[i] === '{') depth += 1;
|
||||||
|
else if (responsive[i] === '}') {
|
||||||
|
depth -= 1;
|
||||||
|
if (depth === 0) {
|
||||||
|
mediaBlocks.push(responsive.slice(match.index + match[0].length, i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const selectors = [...new Set(mediaBlocks.join('\n').match(/\.[a-z][a-z0-9-]*/g) || [])].sort();
|
||||||
|
|
||||||
|
// Properties a responsive rule actually moves. Comparing every property would
|
||||||
|
// drown the signal in font stacks and inherited colour.
|
||||||
|
const PROPERTIES = [
|
||||||
|
'display',
|
||||||
|
'grid-template-columns',
|
||||||
|
'grid-template-rows',
|
||||||
|
'flex-direction',
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'max-width',
|
||||||
|
'padding',
|
||||||
|
'margin',
|
||||||
|
'gap',
|
||||||
|
'font-size',
|
||||||
|
'position',
|
||||||
|
'inset',
|
||||||
|
'overflow',
|
||||||
|
];
|
||||||
|
|
||||||
|
// The legacy pages were deleted at cutover; run this from a pre-cutover
|
||||||
|
// worktree, or the legacy side will 404.
|
||||||
|
const legacyPath = route === 'index' ? 'index.html' : `${route}/index.html`;
|
||||||
|
const astroPath = route === 'index' ? '' : `${route}/`;
|
||||||
|
|
||||||
|
const staging = mkdtempSync(join(tmpdir(), 'af-csd-'));
|
||||||
|
cpSync('dist', join(staging, 'ai-for-dummies'), { recursive: true });
|
||||||
|
|
||||||
|
const freePort = () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const probe = createServer();
|
||||||
|
probe.on('error', reject);
|
||||||
|
probe.listen(0, '127.0.0.1', () => {
|
||||||
|
const { port } = probe.address();
|
||||||
|
probe.close(() => resolve(port));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const legacyPort = await freePort();
|
||||||
|
const astroPort = await freePort();
|
||||||
|
const serve = (dir, port) =>
|
||||||
|
spawn('python3', ['-m', 'http.server', String(port), '-d', dir], { stdio: 'ignore' });
|
||||||
|
const servers = [serve('.', legacyPort), serve(staging, astroPort)];
|
||||||
|
const stop = () => {
|
||||||
|
servers.forEach((s) => s.kill());
|
||||||
|
rmSync(staging, { recursive: true, force: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Every element matching each selector, so a rule that applies to the first
|
||||||
|
// node and not the rest cannot pass.
|
||||||
|
const collect = ([selectors, properties]) => {
|
||||||
|
const out = {};
|
||||||
|
for (const selector of selectors) {
|
||||||
|
const nodes = [...document.querySelectorAll(selector)];
|
||||||
|
out[selector] = nodes.map((node) => {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
return properties
|
||||||
|
.map((property) => `${property}:${style.getPropertyValue(property)}`)
|
||||||
|
.join(';');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
let failures = 0;
|
||||||
|
try {
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
const read = async (url, width) => {
|
||||||
|
const page = await browser.newPage({ viewport: { width, height: 900 } });
|
||||||
|
const response = await page.goto(url, { waitUntil: 'load' });
|
||||||
|
if (!response || !response.ok()) {
|
||||||
|
throw new Error(`${url} returned ${response ? response.status() : 'no response'}`);
|
||||||
|
}
|
||||||
|
await page.waitForTimeout(1500);
|
||||||
|
const styles = await page.evaluate(collect, [selectors, PROPERTIES]);
|
||||||
|
await page.close();
|
||||||
|
return styles;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const width of widths) {
|
||||||
|
const legacy = await read(`http://localhost:${legacyPort}/${legacyPath}`, width);
|
||||||
|
const astro = await read(`http://localhost:${astroPort}/ai-for-dummies/${astroPath}`, width);
|
||||||
|
|
||||||
|
for (const selector of selectors) {
|
||||||
|
const before = legacy[selector];
|
||||||
|
const after = astro[selector];
|
||||||
|
if (before.length === 0 && after.length === 0) continue;
|
||||||
|
if (before.length !== after.length) {
|
||||||
|
console.log(
|
||||||
|
`${width}px ${selector} legacy ${before.length} nodes, astro ${after.length}`,
|
||||||
|
);
|
||||||
|
failures += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let reported = 0;
|
||||||
|
before.forEach((expected, index) => {
|
||||||
|
if (expected === after[index]) return;
|
||||||
|
failures += 1;
|
||||||
|
// Three examples is enough to identify a rule that did not apply.
|
||||||
|
reported += 1;
|
||||||
|
if (reported > 3) return;
|
||||||
|
const differing = expected
|
||||||
|
.split(';')
|
||||||
|
.filter((pair, i) => pair !== after[index].split(';')[i]);
|
||||||
|
const got = after[index].split(';').filter((pair, i) => pair !== expected.split(';')[i]);
|
||||||
|
console.log(`${width}px ${selector}[${index}]`);
|
||||||
|
console.log(` legacy ${differing.join(' ')}`);
|
||||||
|
console.log(` astro ${got.join(' ')}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
console.log(failures === 0 ? 'computed styles match' : `${failures} computed-style differences`);
|
||||||
|
process.exitCode = failures === 0 ? 0 : 1;
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
@@ -51,8 +51,19 @@ pnpm run verify
|
|||||||
# The assertion count is the thing agents are most tempted to "fix" downward.
|
# The assertion count is the thing agents are most tempted to "fix" downward.
|
||||||
# Compare against origin/main and refuse a silent reduction.
|
# Compare against origin/main and refuse a silent reduction.
|
||||||
step "assertion coverage"
|
step "assertion coverage"
|
||||||
|
# Task 19 restored the 42 legacy facts and added ten output snapshots: 84 is the
|
||||||
|
# floor, in addition to whatever origin/main currently requires.
|
||||||
|
#
|
||||||
|
# A removal is allowed only by writing a reason into the ledger. The gate counts
|
||||||
|
# its entries and lowers the bar by exactly that many, so the bar cannot move
|
||||||
|
# without a visible diff explaining why. Deleting an entry to buy headroom is
|
||||||
|
# the same offence as deleting the assertion was.
|
||||||
|
ledger=.agents/context/assertion-removals.md
|
||||||
current=$(grep -c 'throw new Error' scripts/verify.mjs)
|
current=$(grep -c 'throw new Error' scripts/verify.mjs)
|
||||||
baseline=$(git show origin/main:scripts/verify.mjs 2>/dev/null | grep -c 'throw new Error' || echo "$current")
|
allowed=$(grep -c '^## ' "$ledger" 2>/dev/null || echo 0)
|
||||||
|
baseline=$(git show origin/main:scripts/verify.mjs 2>/dev/null | grep -c 'throw new Error' || echo 0)
|
||||||
|
if [ "$baseline" -lt 84 ]; then baseline=84; fi
|
||||||
|
baseline=$((baseline - allowed))
|
||||||
if [ "$current" -lt "$baseline" ]; then
|
if [ "$current" -lt "$baseline" ]; then
|
||||||
echo "gate: verify.mjs coverage fell from $baseline to $current assertions." >&2
|
echo "gate: verify.mjs coverage fell from $baseline to $current assertions." >&2
|
||||||
echo " Only verification-engineer may reduce it, with a reason per removal." >&2
|
echo " Only verification-engineer may reduce it, with a reason per removal." >&2
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
#!/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.
|
||||||
|
//
|
||||||
|
// The legacy pages were deleted at cutover, so this needs a pre-cutover tree:
|
||||||
|
// git worktree add /tmp/vanilla <pre-cutover-sha>
|
||||||
|
// and run from there, or run it from a checkout that still has them.
|
||||||
|
import { spawn } from 'node:child_process';
|
||||||
|
import { cpSync, mkdtempSync, rmSync } from 'node:fs';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
import { createServer } from 'node:net';
|
||||||
|
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 });
|
||||||
|
|
||||||
|
// Ask the kernel for a free port rather than pinning one. Back-to-back runs
|
||||||
|
// used to collide: the previous run's server was still holding the fixed port
|
||||||
|
// while its staging directory had already been deleted, so every page came
|
||||||
|
// back as a 404 and the diff reported the whole route missing.
|
||||||
|
const freePort = () =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const probe = createServer();
|
||||||
|
probe.on('error', reject);
|
||||||
|
probe.listen(0, '127.0.0.1', () => {
|
||||||
|
const { port } = probe.address();
|
||||||
|
probe.close(() => resolve(port));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const legacyPort = await freePort();
|
||||||
|
const astroPort = await freePort();
|
||||||
|
|
||||||
|
const serve = (dir, port) =>
|
||||||
|
spawn('python3', ['-m', 'http.server', String(port), '-d', dir], { stdio: 'ignore' });
|
||||||
|
const servers = [serve('.', legacyPort), serve(staging, astroPort)];
|
||||||
|
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 } });
|
||||||
|
const response = await page.goto(url, { waitUntil: 'load' });
|
||||||
|
// A 404 renders as four spans of python's error page and the diff then
|
||||||
|
// reports the entire route as missing, which reads exactly like a real
|
||||||
|
// regression. Fail loudly instead.
|
||||||
|
if (!response || !response.ok()) {
|
||||||
|
throw new Error(`${url} returned ${response ? response.status() : 'no response'}`);
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
// Islands hydrate at their own pace, and the language toggle repaints in
|
||||||
|
// more than one frame. A single read after a fixed wait is flaky, so read
|
||||||
|
// until two consecutive reads agree.
|
||||||
|
let spans = await page.evaluate(visibleText);
|
||||||
|
for (let i = 0; i < 10; i += 1) {
|
||||||
|
await page.waitForTimeout(300);
|
||||||
|
const next = await page.evaluate(visibleText);
|
||||||
|
if (next.length === spans.length && next.every((span, j) => span === spans[j])) {
|
||||||
|
spans = next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spans = next;
|
||||||
|
}
|
||||||
|
await page.close();
|
||||||
|
return spans;
|
||||||
|
};
|
||||||
|
|
||||||
|
const legacy = await grab(`http://localhost:${legacyPort}/${legacyPath}`);
|
||||||
|
const astro = await grab(`http://localhost:${astroPort}/ai-for-dummies/${astroPath}`);
|
||||||
|
await browser.close();
|
||||||
|
|
||||||
|
// Count occurrences, not membership. A set comparison reports zero when a
|
||||||
|
// string the legacy page paints four times is painted three times here --
|
||||||
|
// exactly the kind of near-miss that got past the earlier checks.
|
||||||
|
const tally = (spans) => {
|
||||||
|
const counts = new Map();
|
||||||
|
for (const span of spans) counts.set(span, (counts.get(span) || 0) + 1);
|
||||||
|
return counts;
|
||||||
|
};
|
||||||
|
|
||||||
|
const legacyCounts = tally(legacy);
|
||||||
|
const astroCounts = tally(astro);
|
||||||
|
|
||||||
|
const missing = [];
|
||||||
|
for (const [span, count] of legacyCounts) {
|
||||||
|
const short = count - (astroCounts.get(span) || 0);
|
||||||
|
for (let i = 0; i < short; i += 1) missing.push(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 extra = [];
|
||||||
|
for (const [span, count] of astroCounts) {
|
||||||
|
const over = count - (legacyCounts.get(span) || 0);
|
||||||
|
for (let i = 0; i < over; i += 1) extra.push(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Order counts too. Both pages can paint the same strings while a block
|
||||||
|
// sits in the wrong place -- the Portuguese eyebrow, or a reordered card
|
||||||
|
// deck -- and a count-only comparison calls that clean.
|
||||||
|
const firstOutOfOrder = legacy.findIndex((span, i) => astro[i] !== 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}`);
|
||||||
|
if (firstOutOfOrder !== -1) {
|
||||||
|
console.log(` order diverges at span ${firstOutOfOrder}`);
|
||||||
|
console.log(` legacy: ${legacy[firstOutOfOrder]}`);
|
||||||
|
console.log(` astro: ${astro[firstOutOfOrder]}`);
|
||||||
|
}
|
||||||
|
process.exitCode = missing.length === 0 && extra.length === 0 && firstOutOfOrder === -1 ? 0 : 1;
|
||||||
|
} finally {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
@@ -12,8 +12,12 @@ description:
|
|||||||
|
|
||||||
The snapshot is the only objective evidence that no content was lost.
|
The snapshot is the only objective evidence that no content was lost.
|
||||||
|
|
||||||
|
The vanilla site was deleted at cutover. To compare against it, check the
|
||||||
|
pre-cutover tree out into a scratch worktree first:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run serve & # vanilla site on :4173
|
git worktree add /tmp/vanilla <pre-cutover-sha>
|
||||||
|
(cd /tmp/vanilla && python3 -m http.server 4173) &
|
||||||
node .agents/scripts/snapshot-route.mjs http://localhost:4173/models/ \
|
node .agents/scripts/snapshot-route.mjs http://localhost:4173/models/ \
|
||||||
> .agents/snapshots/models.txt
|
> .agents/snapshots/models.txt
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ a bug.
|
|||||||
```bash
|
```bash
|
||||||
python3 - <<'PY'
|
python3 - <<'PY'
|
||||||
import re
|
import re
|
||||||
files=['styles.css','chapters.css','landing.css','rules/styles.css','skills/styles.css',
|
files=['legacy/styles/guide.css','legacy/styles/chapters.css','legacy/styles/skills.css',
|
||||||
'skills-review/styles.css','hands-on/starter/styles.css','hands-on/rules/styles.css']
|
'legacy/styles/skills-review.css','legacy/styles/change-lens.css',
|
||||||
|
'legacy/styles/audit.css','public/hands-on/starter/styles.css',
|
||||||
|
'public/hands-on/rules/styles.css']
|
||||||
seen={}
|
seen={}
|
||||||
for f in files:
|
for f in files:
|
||||||
for m in re.finditer(r'--([a-z-]+):\s*([^;}]+)', open(f).read()):
|
for m in re.finditer(r'--([a-z-]+):\s*([^;}]+)', open(f).read()):
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ with sync_playwright() as p:
|
|||||||
browser.close()
|
browser.close()
|
||||||
```
|
```
|
||||||
|
|
||||||
Run once against the vanilla site (`pnpm run serve`), once against
|
Run once against `pnpm run preview`. To compare against the vanilla site, serve
|
||||||
`pnpm run preview`. Keep both sets.
|
a pre-cutover worktree on :4173 first — those files are no longer on `main`.
|
||||||
|
Keep both sets.
|
||||||
|
|
||||||
## Compare
|
## Compare
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 444 KiB |
|
Before Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 465 KiB |
|
Before Width: | Height: | Size: 485 KiB |
|
Before Width: | Height: | Size: 438 KiB |
|
Before Width: | Height: | Size: 458 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 444 KiB |
|
Before Width: | Height: | Size: 341 KiB |
|
Before Width: | Height: | Size: 342 KiB |
|
Before Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 465 KiB |
|
Before Width: | Height: | Size: 485 KiB |
|
Before Width: | Height: | Size: 438 KiB |
|
Before Width: | Height: | Size: 458 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 104 KiB |
@@ -0,0 +1,303 @@
|
|||||||
|
{
|
||||||
|
"colors": [
|
||||||
|
"#081621",
|
||||||
|
"#0b1b27",
|
||||||
|
"#0c1a25",
|
||||||
|
"#0f2230",
|
||||||
|
"#102536",
|
||||||
|
"#102837",
|
||||||
|
"#102b3a",
|
||||||
|
"#112a3b",
|
||||||
|
"#122534",
|
||||||
|
"#123042",
|
||||||
|
"#132b3b",
|
||||||
|
"#172f42",
|
||||||
|
"#173046",
|
||||||
|
"#173245",
|
||||||
|
"#173b4f",
|
||||||
|
"#18364a",
|
||||||
|
"#19364a",
|
||||||
|
"#1a4b42",
|
||||||
|
"#1c425a",
|
||||||
|
"#1d455b",
|
||||||
|
"#1f3a4b",
|
||||||
|
"#215675",
|
||||||
|
"#244760",
|
||||||
|
"#29455a",
|
||||||
|
"#2a4150",
|
||||||
|
"#315f80",
|
||||||
|
"#344c5d",
|
||||||
|
"#41596b",
|
||||||
|
"#426070",
|
||||||
|
"#466274",
|
||||||
|
"#486175",
|
||||||
|
"#496274",
|
||||||
|
"#527085",
|
||||||
|
"#527f9f",
|
||||||
|
"#557080",
|
||||||
|
"#572f32",
|
||||||
|
"#596f9a",
|
||||||
|
"#5b7098",
|
||||||
|
"#65717a",
|
||||||
|
"#697b89",
|
||||||
|
"#6b668f",
|
||||||
|
"#6c6898",
|
||||||
|
"#7c78a8",
|
||||||
|
"#80c69a",
|
||||||
|
"#80c69a20",
|
||||||
|
"#80c69a22",
|
||||||
|
"#8ca1af",
|
||||||
|
"#91aab7",
|
||||||
|
"#9ba7a5",
|
||||||
|
"#9bcba7",
|
||||||
|
"#9eabb4",
|
||||||
|
"#9eb0bb",
|
||||||
|
"#a7483f",
|
||||||
|
"#a9b6be",
|
||||||
|
"#a9bcc8",
|
||||||
|
"#a9e3ae",
|
||||||
|
"#aebbc3",
|
||||||
|
"#aebfc7",
|
||||||
|
"#aebfc9",
|
||||||
|
"#afbec7",
|
||||||
|
"#b0bac1",
|
||||||
|
"#b5c0c7",
|
||||||
|
"#b7c7d1",
|
||||||
|
"#b8c8d2",
|
||||||
|
"#b9c8d0",
|
||||||
|
"#b9c8d1",
|
||||||
|
"#bed0dc",
|
||||||
|
"#bfccd4",
|
||||||
|
"#c1d1d8",
|
||||||
|
"#c4cdd3",
|
||||||
|
"#c6d2d7",
|
||||||
|
"#c9d5dc",
|
||||||
|
"#cbd9e1",
|
||||||
|
"#d0d5d2",
|
||||||
|
"#d4dfe3",
|
||||||
|
"#d5dde2",
|
||||||
|
"#d5f1d6",
|
||||||
|
"#d6e1e4",
|
||||||
|
"#d8dee2",
|
||||||
|
"#e5e3ef",
|
||||||
|
"#e5eeeb",
|
||||||
|
"#e89a8e",
|
||||||
|
"#e8ecee",
|
||||||
|
"#e9ecee",
|
||||||
|
"#e9eeed",
|
||||||
|
"#ebbf58",
|
||||||
|
"#eceaf5",
|
||||||
|
"#eceff0",
|
||||||
|
"#edf0f1",
|
||||||
|
"#eeedf6",
|
||||||
|
"#efc76b",
|
||||||
|
"#efc76b18",
|
||||||
|
"#f0eef8",
|
||||||
|
"#f1f0f7",
|
||||||
|
"#f5f4f1",
|
||||||
|
"#f6f3ed",
|
||||||
|
"#ffb5a8",
|
||||||
|
"#ffd7d0",
|
||||||
|
"#fff",
|
||||||
|
"#ffffff05",
|
||||||
|
"#ffffff06",
|
||||||
|
"#ffffff1f",
|
||||||
|
"#ffffff2b",
|
||||||
|
"#ffffff2d",
|
||||||
|
"#ffffff30",
|
||||||
|
"#ffffff32",
|
||||||
|
"#ffffff40",
|
||||||
|
"#ffffff42",
|
||||||
|
"#ffffff50",
|
||||||
|
"#ffffff66",
|
||||||
|
"rgb(255 255 255 / 14.1176%)",
|
||||||
|
"rgb(255 255 255 / 22.7451%)",
|
||||||
|
"rgb(255 255 255 / 25.098%)",
|
||||||
|
"rgb(255 255 255 / 31.3725%)"
|
||||||
|
],
|
||||||
|
"sizes": [
|
||||||
|
"01em",
|
||||||
|
"1em",
|
||||||
|
"1px",
|
||||||
|
"1.2em",
|
||||||
|
"1.45em",
|
||||||
|
"1.5em",
|
||||||
|
"1.8em",
|
||||||
|
"02em",
|
||||||
|
"2px",
|
||||||
|
"2.4vw",
|
||||||
|
"2.5vw",
|
||||||
|
"2.6vw",
|
||||||
|
"3em",
|
||||||
|
"3px",
|
||||||
|
"3vw",
|
||||||
|
"3.3vw",
|
||||||
|
"3.4vw",
|
||||||
|
"04em",
|
||||||
|
"4px",
|
||||||
|
"4vw",
|
||||||
|
"05em",
|
||||||
|
"5em",
|
||||||
|
"5px",
|
||||||
|
"5vw",
|
||||||
|
"5.6vw",
|
||||||
|
"06em",
|
||||||
|
"6px",
|
||||||
|
"6vw",
|
||||||
|
"07em",
|
||||||
|
"7px",
|
||||||
|
"7vw",
|
||||||
|
"08em",
|
||||||
|
"8px",
|
||||||
|
"8vw",
|
||||||
|
"8.3vw",
|
||||||
|
"09em",
|
||||||
|
"9em",
|
||||||
|
"9px",
|
||||||
|
"9vw",
|
||||||
|
"10px",
|
||||||
|
"10.5px",
|
||||||
|
"11px",
|
||||||
|
"12em",
|
||||||
|
"12px",
|
||||||
|
"12vh",
|
||||||
|
"13px",
|
||||||
|
"14px",
|
||||||
|
"15px",
|
||||||
|
"16px",
|
||||||
|
"17px",
|
||||||
|
"17vw",
|
||||||
|
"18px",
|
||||||
|
"19px",
|
||||||
|
"20px",
|
||||||
|
"22px",
|
||||||
|
"23px",
|
||||||
|
"24px",
|
||||||
|
"25px",
|
||||||
|
"26px",
|
||||||
|
"27px",
|
||||||
|
"28px",
|
||||||
|
"30px",
|
||||||
|
"32px",
|
||||||
|
"34px",
|
||||||
|
"35em",
|
||||||
|
"35px",
|
||||||
|
"36px",
|
||||||
|
"38px",
|
||||||
|
"40px",
|
||||||
|
"42px",
|
||||||
|
"44px",
|
||||||
|
"045em",
|
||||||
|
"45px",
|
||||||
|
"46px",
|
||||||
|
"48px",
|
||||||
|
"50px",
|
||||||
|
"52px",
|
||||||
|
"54px",
|
||||||
|
"55px",
|
||||||
|
"56px",
|
||||||
|
"58px",
|
||||||
|
"60px",
|
||||||
|
"62px",
|
||||||
|
"64px",
|
||||||
|
"65px",
|
||||||
|
"70px",
|
||||||
|
"72px",
|
||||||
|
"075em",
|
||||||
|
"75px",
|
||||||
|
"76px",
|
||||||
|
"78px",
|
||||||
|
"80px",
|
||||||
|
"82px",
|
||||||
|
"85px",
|
||||||
|
"90px",
|
||||||
|
"92px",
|
||||||
|
"95px",
|
||||||
|
"96px",
|
||||||
|
"100px",
|
||||||
|
"105px",
|
||||||
|
"108px",
|
||||||
|
"110px",
|
||||||
|
"112px",
|
||||||
|
"120px",
|
||||||
|
"126px",
|
||||||
|
"130px",
|
||||||
|
"135px",
|
||||||
|
"140px",
|
||||||
|
"145px",
|
||||||
|
"148px",
|
||||||
|
"150px",
|
||||||
|
"160px",
|
||||||
|
"164px",
|
||||||
|
"170px",
|
||||||
|
"175px",
|
||||||
|
"180px",
|
||||||
|
"190px",
|
||||||
|
"200px",
|
||||||
|
"210px",
|
||||||
|
"220px",
|
||||||
|
"230px",
|
||||||
|
"240px",
|
||||||
|
"255px",
|
||||||
|
"260px",
|
||||||
|
"270px",
|
||||||
|
"280px",
|
||||||
|
"290px",
|
||||||
|
"300px",
|
||||||
|
"305px",
|
||||||
|
"320px",
|
||||||
|
"330px",
|
||||||
|
"340px",
|
||||||
|
"360px",
|
||||||
|
"380px",
|
||||||
|
"390px",
|
||||||
|
"410px",
|
||||||
|
"420px",
|
||||||
|
"440px",
|
||||||
|
"460px",
|
||||||
|
"500px",
|
||||||
|
"520px",
|
||||||
|
"530px",
|
||||||
|
"540px",
|
||||||
|
"560px",
|
||||||
|
"570px",
|
||||||
|
"600px",
|
||||||
|
"620px",
|
||||||
|
"650px",
|
||||||
|
"680px",
|
||||||
|
"700px",
|
||||||
|
"720px",
|
||||||
|
"730px",
|
||||||
|
"750px",
|
||||||
|
"780px",
|
||||||
|
"800px",
|
||||||
|
"850px",
|
||||||
|
"880px",
|
||||||
|
"900px",
|
||||||
|
"950px",
|
||||||
|
"1000px",
|
||||||
|
"1040px",
|
||||||
|
"1050px",
|
||||||
|
"1100px",
|
||||||
|
"1400px",
|
||||||
|
"1420px",
|
||||||
|
"1500px",
|
||||||
|
"1600px",
|
||||||
|
"1840px",
|
||||||
|
"1920px",
|
||||||
|
"1960px",
|
||||||
|
"2200px",
|
||||||
|
"2880px"
|
||||||
|
],
|
||||||
|
"breakpoints": [
|
||||||
|
"520px",
|
||||||
|
"560px",
|
||||||
|
"600px",
|
||||||
|
"800px",
|
||||||
|
"880px",
|
||||||
|
"1050px",
|
||||||
|
"1100px",
|
||||||
|
"1600px",
|
||||||
|
"2200px"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
[
|
||||||
|
"01 frota",
|
||||||
|
"02 worktrees",
|
||||||
|
"03 modelos",
|
||||||
|
"04 skills",
|
||||||
|
"05 criar",
|
||||||
|
"06 kit de campo",
|
||||||
|
"07 prática",
|
||||||
|
"ENGENHARIA DE IA <i></i> 01 / 2026",
|
||||||
|
"Uma apresentação para quem entrega software",
|
||||||
|
"Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.",
|
||||||
|
"NOTA DE CAMPO / 001",
|
||||||
|
"Entregue o<br /><em>sistema.</em>",
|
||||||
|
"Skills · agentes · worktrees · evidências",
|
||||||
|
"modelo forte<br />para ambiguidade",
|
||||||
|
"workers delimitados<br />em paralelo",
|
||||||
|
"iterações<br />com evidências",
|
||||||
|
"Leia isto como um mapa de rota, não como uma receita de prompt.",
|
||||||
|
"REGRA ZERO",
|
||||||
|
"Modelo forte para ambiguidade.<br />Modelo leve para trabalho delimitado.",
|
||||||
|
"Uma pequena frota",
|
||||||
|
"coordenação antes do paralelismo",
|
||||||
|
"ORQUESTRADOR",
|
||||||
|
"Decide o que<br />precisa acontecer.",
|
||||||
|
"Componentes e estados visuais",
|
||||||
|
"Casos de aceitação",
|
||||||
|
"Guia e exemplos",
|
||||||
|
"O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados verificáveis. Ele não precisa digitar cada linha.",
|
||||||
|
"Por que a fronteira importa",
|
||||||
|
"uma tarefa vaga / três falhas previsíveis",
|
||||||
|
"Sopa de contexto",
|
||||||
|
"Cada worker lê tudo. Ninguém sabe quais fatos são essenciais.",
|
||||||
|
"Colisão de branches",
|
||||||
|
"Dois agentes usam o mesmo checkout. O caminho mais rápido vira resolução de conflitos.",
|
||||||
|
"Desvio confiante",
|
||||||
|
"O diff parece ótimo, mas ninguém verifica se resolveu o problema original.",
|
||||||
|
"O ciclo de subagentes",
|
||||||
|
"Clique em uma fase.<br /><em>Veja a passagem.</em>",
|
||||||
|
"Delegar é mover uma tarefa delimitada para um contexto menor — não abrir mão da responsabilidade.",
|
||||||
|
"O que atravessa contextos",
|
||||||
|
"brief → diff → evidência",
|
||||||
|
"Pacote",
|
||||||
|
"Contém",
|
||||||
|
"Por que importa",
|
||||||
|
"Git worktrees",
|
||||||
|
"Uma branch<br />por <em>mão.</em>",
|
||||||
|
"Um worktree é outro diretório ligado ao mesmo repositório. Cada agente recebe seu próprio checkout e índice; o histórico continua compartilhado.",
|
||||||
|
"Selecione um nó para inspecionar checkout, responsável e próxima ação.",
|
||||||
|
"topologia do repositório",
|
||||||
|
"<i></i> 4 checkouts",
|
||||||
|
"RAIZ",
|
||||||
|
"AGENTE DE UI",
|
||||||
|
"AGENTE DE TESTES",
|
||||||
|
"AGENTE DE DOCS",
|
||||||
|
"● limpo",
|
||||||
|
"3 arquivos · trabalhando",
|
||||||
|
"8 verificações · pronto",
|
||||||
|
"2 páginas · revisão",
|
||||||
|
"Roteamento de modelos",
|
||||||
|
"Não pague por<br />raciocínio onde precisa<br />de <em>ritmo.</em>",
|
||||||
|
"Escolha um trabalho para entender por que o perfil do modelo muda.",
|
||||||
|
"Trabalho",
|
||||||
|
"Perfil",
|
||||||
|
"Formato do prompt",
|
||||||
|
"Planejar",
|
||||||
|
"Construir",
|
||||||
|
"Explorar",
|
||||||
|
"Revisar",
|
||||||
|
"Skills",
|
||||||
|
"Escreva do jeito certo<br /><em>uma vez.</em>",
|
||||||
|
"Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências, scripts e assets. Não é memória mágica e não substitui critérios de aceitação.",
|
||||||
|
"01 / defina o gatilho",
|
||||||
|
"02 / carregue detalhes sob demanda",
|
||||||
|
"03 / devolva evidências",
|
||||||
|
"PACOTE DE SKILL",
|
||||||
|
"Skills comuns",
|
||||||
|
"escolha o comportamento antes do modelo",
|
||||||
|
"O kit de campo",
|
||||||
|
"Trabalhos diferentes.<br />Instintos <em>diferentes.</em>",
|
||||||
|
"Uma skill muda como o agente aborda o trabalho. Algumas moldam a comunicação. Outras impõem pesquisa, diagnóstico, revisão ou disciplina de conclusão. Selecione uma para inspecionar sua regra operacional.",
|
||||||
|
"SIMPLIFICAR",
|
||||||
|
"código mínimo que funciona",
|
||||||
|
"COMUNICAR",
|
||||||
|
"sinal sem excesso",
|
||||||
|
"CONCLUIR",
|
||||||
|
"gates e evidências",
|
||||||
|
"INVESTIGAR",
|
||||||
|
"fontes primárias primeiro",
|
||||||
|
"DIAGNOSTICAR",
|
||||||
|
"ciclo curto de feedback",
|
||||||
|
"REVISAR",
|
||||||
|
"padrões × especificação",
|
||||||
|
"ECONOMIZAR",
|
||||||
|
"comprima saídas ruidosas",
|
||||||
|
"UM LOADOUT PRÁTICO",
|
||||||
|
"<b>PLANEJAR</b> unlazy <i>→</i> <b>CONSTRUIR</b> ponytail-lite <i>→</i> <b>DIAGNOSTICAR</b> diagnosing-bugs <i>→</i> <b>REPORTAR</b> caveman",
|
||||||
|
"O PAPEL HUMANO",
|
||||||
|
"O agente pode ser autônomo na execução. Intenção, limites e evidências continuam sendo seus.",
|
||||||
|
"COMECE AQUI",
|
||||||
|
"Comece com um agente e uma skill. Adicione paralelismo apenas quando as tarefas forem realmente independentes.",
|
||||||
|
"Continue aprendendo",
|
||||||
|
"12 novas leituras + documentação primária",
|
||||||
|
"Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes. <a href=\"rules/\">Estudo de caso sobre regras e enforcement →</a> <a href=\"docs/references/README.md\">Referências primárias →</a> <a href=\"docs/references/additional-reading.md\">Trilha com 12 leituras →</a>"
|
||||||
|
]
|
||||||
@@ -28,8 +28,11 @@ A presentation for humans who ship
|
|||||||
Uma apresentação para quem entrega software
|
Uma apresentação para quem entrega software
|
||||||
AI for
|
AI for
|
||||||
dummies.
|
dummies.
|
||||||
You do not need an army of models. You need a system: one mind to frame the work, several hands to execute it, and a clean boundary between every task.
|
You do not need an army of models. You need a system: one mind to frame the work,
|
||||||
Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.
|
several hands to execute it, and a clean boundary between every task.
|
||||||
|
Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para
|
||||||
|
enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada
|
||||||
|
tarefa.
|
||||||
FIELD NOTE / 001
|
FIELD NOTE / 001
|
||||||
NOTA DE CAMPO / 001
|
NOTA DE CAMPO / 001
|
||||||
Ship the
|
Ship the
|
||||||
@@ -39,8 +42,8 @@ sistema.
|
|||||||
Skills · agents · worktrees · proof
|
Skills · agents · worktrees · proof
|
||||||
Skills · agentes · worktrees · evidências
|
Skills · agentes · worktrees · evidências
|
||||||
Uma apresentação para quem entrega software
|
Uma apresentação para quem entrega software
|
||||||
IA para
|
AI for
|
||||||
iniciantes.
|
dummies.
|
||||||
Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para
|
Você não precisa de um exército de modelos. Precisa de um sistema: uma mente para
|
||||||
enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.
|
enquadrar o trabalho, várias mãos para executá-lo e uma fronteira clara entre cada tarefa.
|
||||||
NOTA DE CAMPO / 001
|
NOTA DE CAMPO / 001
|
||||||
@@ -85,8 +88,8 @@ MAKE
|
|||||||
REGRA ZERO
|
REGRA ZERO
|
||||||
Modelo forte para ambiguidade.
|
Modelo forte para ambiguidade.
|
||||||
Modelo leve para trabalho delimitado.
|
Modelo leve para trabalho delimitado.
|
||||||
PENSE
|
THINK
|
||||||
FAÇA
|
MAKE
|
||||||
A small fleet
|
A small fleet
|
||||||
Uma pequena frota
|
Uma pequena frota
|
||||||
coordination before parallelism
|
coordination before parallelism
|
||||||
@@ -114,8 +117,10 @@ agent/docs
|
|||||||
Interface worker
|
Interface worker
|
||||||
Receives: component contract + visual states
|
Receives: component contract + visual states
|
||||||
Returns: focused diff + viewport evidence
|
Returns: focused diff + viewport evidence
|
||||||
The orchestrator preserves intent, writes small contracts, and gathers results that can be
|
The orchestrator preserves intent, writes small contracts, and gathers results that can
|
||||||
verified. It does not need to type every line.
|
be verified. It does not need to type every line.
|
||||||
|
O orquestrador preserva a intenção, escreve pequenos contratos e reúne resultados
|
||||||
|
verificáveis. Ele não precisa digitar cada linha.
|
||||||
Why the boundary matters
|
Why the boundary matters
|
||||||
Por que a fronteira importa
|
Por que a fronteira importa
|
||||||
one vague task / three predictable failures
|
one vague task / three predictable failures
|
||||||
@@ -129,7 +134,8 @@ Cada worker lê tudo. Ninguém sabe quais fatos são essenciais.
|
|||||||
Branch collision
|
Branch collision
|
||||||
Colisão de branches
|
Colisão de branches
|
||||||
Two agents touch the same checkout. The fastest path becomes conflict resolution.
|
Two agents touch the same checkout. The fastest path becomes conflict resolution.
|
||||||
Dois agentes usam o mesmo checkout. O caminho mais rápido vira resolução de conflitos.
|
Dois agentes usam o mesmo checkout. O caminho mais rápido vira resolução de
|
||||||
|
conflitos.
|
||||||
03
|
03
|
||||||
Confident drift
|
Confident drift
|
||||||
Desvio confiante
|
Desvio confiante
|
||||||
@@ -143,6 +149,8 @@ Clique em uma fase.
|
|||||||
Veja a passagem.
|
Veja a passagem.
|
||||||
Delegation means moving one bounded task into a smaller context—not giving away
|
Delegation means moving one bounded task into a smaller context—not giving away
|
||||||
responsibility.
|
responsibility.
|
||||||
|
Delegar é mover uma tarefa delimitada para um contexto menor — não abrir mão da
|
||||||
|
responsabilidade.
|
||||||
01
|
01
|
||||||
PLAN
|
PLAN
|
||||||
02
|
02
|
||||||
@@ -186,6 +194,8 @@ por
|
|||||||
mão.
|
mão.
|
||||||
A worktree is another directory linked to the same repository. Each agent gets its own
|
A worktree is another directory linked to the same repository. Each agent gets its own
|
||||||
checkout and index; history remains shared.
|
checkout and index; history remains shared.
|
||||||
|
Um worktree é outro diretório ligado ao mesmo repositório. Cada agente recebe seu
|
||||||
|
próprio checkout e índice; o histórico continua compartilhado.
|
||||||
Select a node to inspect its checkout, owner, and next action.
|
Select a node to inspect its checkout, owner, and next action.
|
||||||
Selecione um nó para inspecionar checkout, responsável e próxima ação.
|
Selecione um nó para inspecionar checkout, responsável e próxima ação.
|
||||||
repository topology
|
repository topology
|
||||||
@@ -193,8 +203,10 @@ topologia do repositório
|
|||||||
4 checkouts
|
4 checkouts
|
||||||
4 checkouts
|
4 checkouts
|
||||||
ROOT
|
ROOT
|
||||||
|
RAIZ
|
||||||
main
|
main
|
||||||
● clean
|
● clean
|
||||||
|
● limpo
|
||||||
UI AGENT
|
UI AGENT
|
||||||
AGENTE DE UI
|
AGENTE DE UI
|
||||||
agent/ui
|
agent/ui
|
||||||
@@ -229,8 +241,11 @@ ritmo.
|
|||||||
Choose a job to see why the model profile changes.
|
Choose a job to see why the model profile changes.
|
||||||
Escolha um trabalho para entender por que o perfil do modelo muda.
|
Escolha um trabalho para entender por que o perfil do modelo muda.
|
||||||
Work
|
Work
|
||||||
|
Trabalho
|
||||||
Profile
|
Profile
|
||||||
|
Perfil
|
||||||
Prompt shape
|
Prompt shape
|
||||||
|
Formato do prompt
|
||||||
Plan
|
Plan
|
||||||
Planejar
|
Planejar
|
||||||
strong / broad
|
strong / broad
|
||||||
@@ -262,8 +277,12 @@ gear.
|
|||||||
Escolha o motor.
|
Escolha o motor.
|
||||||
Depois escolha a
|
Depois escolha a
|
||||||
marcha.
|
marcha.
|
||||||
A stronger model changes the capability ceiling. Higher reasoning effort gives that model more room to work. Start with the lightest combination that passes your real checks, then move one knob at a time.
|
A stronger model changes the capability ceiling. Higher reasoning effort gives that
|
||||||
Um modelo mais forte muda o teto de capacidade. Mais esforço de raciocínio dá mais espaço para esse modelo trabalhar. Comece com a combinação mais leve que passa seus checks e mova um controle por vez.
|
model more room to work. Start with the lightest combination that passes your real
|
||||||
|
checks, then move one knob at a time.
|
||||||
|
Um modelo mais forte muda o teto de capacidade. Mais esforço de raciocínio dá mais
|
||||||
|
espaço para esse modelo trabalhar. Comece com a combinação mais leve que passa seus
|
||||||
|
checks e mova um controle por vez.
|
||||||
OPENAI
|
OPENAI
|
||||||
CLAUDE
|
CLAUDE
|
||||||
GEMINI
|
GEMINI
|
||||||
@@ -278,6 +297,9 @@ BAIXO
|
|||||||
bounded + fast
|
bounded + fast
|
||||||
delimitado + rápido
|
delimitado + rápido
|
||||||
MEDIUM
|
MEDIUM
|
||||||
|
MÉDIO
|
||||||
|
default start
|
||||||
|
ponto inicial
|
||||||
HIGH
|
HIGH
|
||||||
ALTO
|
ALTO
|
||||||
complex + costly
|
complex + costly
|
||||||
@@ -287,17 +309,29 @@ Balanced starting point for normal implementation, tests, and review. Measure be
|
|||||||
reasoning: { effort: "medium" }
|
reasoning: { effort: "medium" }
|
||||||
ROUTING RULE
|
ROUTING RULE
|
||||||
REGRA DE ROTEAMENTO
|
REGRA DE ROTEAMENTO
|
||||||
Use strong models for ambiguity and judgment. Use lighter models for bounded execution. Raise effort only when evaluation shows a gain.
|
Use strong models for ambiguity and judgment. Use lighter models for bounded execution.
|
||||||
Use modelos fortes para ambiguidade e julgamento. Use modelos leves para execução delimitada. Aumente o esforço apenas quando a avaliação mostrar ganho.
|
Raise effort only when evaluation shows a gain.
|
||||||
Skills
|
Use modelos fortes para ambiguidade e julgamento. Use modelos leves para execução
|
||||||
|
delimitada. Aumente o esforço apenas quando a avaliação mostrar ganho.
|
||||||
Skills
|
Skills
|
||||||
|
Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências,
|
||||||
|
scripts e assets. Não é memória mágica e não substitui critérios de aceitação.
|
||||||
Write the right way
|
Write the right way
|
||||||
once.
|
once.
|
||||||
Escreva do jeito certo
|
Escreva do jeito certo
|
||||||
uma vez.
|
uma vez.
|
||||||
A skill is a reusable procedure. It can carry instructions, references, scripts, and assets. It is not magical memory, and it does not replace acceptance criteria.
|
A skill is a reusable procedure. It can carry instructions, references, scripts, and
|
||||||
Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências, scripts e assets. Não é memória mágica e não substitui critérios de aceitação.
|
assets. It is not magical memory, and it does not replace acceptance criteria.
|
||||||
|
Uma skill é um procedimento reutilizável. Ela pode carregar instruções, referências,
|
||||||
|
scripts e assets. Não é memória mágica e não substitui critérios de aceitação.
|
||||||
|
01 / trigger clearly
|
||||||
|
01 / defina o gatilho
|
||||||
|
02 / load detail on demand
|
||||||
|
02 / carregue detalhes sob demanda
|
||||||
|
03 / return evidence
|
||||||
|
03 / devolva evidências
|
||||||
SKILL PACKAGE
|
SKILL PACKAGE
|
||||||
|
PACOTE DE SKILL
|
||||||
SKILL.md
|
SKILL.md
|
||||||
procedure and limits
|
procedure and limits
|
||||||
references/
|
references/
|
||||||
@@ -310,18 +344,48 @@ templates and examples
|
|||||||
SKILL.md
|
SKILL.md
|
||||||
Trigger, procedure, constraints, and the exact evidence the agent must return.
|
Trigger, procedure, constraints, and the exact evidence the agent must return.
|
||||||
select another file to explore
|
select another file to explore
|
||||||
|
name: review-ui · check focus, mobile, reduced motion · run verification · return evidence
|
||||||
Create a skill
|
Create a skill
|
||||||
|
Criar uma skill
|
||||||
repeatable pain → reusable judgment
|
repeatable pain → reusable judgment
|
||||||
03
|
atrito repetido → julgamento reutilizável
|
||||||
Choose only useful anatomy
|
The skill forge
|
||||||
|
A forja de skills
|
||||||
|
Teach the decision.
|
||||||
|
Keep the context
|
||||||
|
light.
|
||||||
|
Ensine a decisão.
|
||||||
|
Mantenha o contexto
|
||||||
|
leve.
|
||||||
|
Do not package everything you know. Capture the non-obvious choices that repeatedly
|
||||||
|
improve an outcome, then prove the skill changes behavior.
|
||||||
|
Não empacote tudo o que você sabe. Capture as escolhas não óbvias que melhoram
|
||||||
|
resultados repetidamente e prove que a skill muda o comportamento.
|
||||||
01
|
01
|
||||||
Start from repeated friction
|
Observe
|
||||||
05
|
Observar
|
||||||
Test behavior, then sharpen
|
find repeated friction
|
||||||
|
encontre atrito repetido
|
||||||
02
|
02
|
||||||
Make discovery precise
|
Define trigger
|
||||||
|
Definir gatilho
|
||||||
|
route precisely
|
||||||
|
roteie com precisão
|
||||||
|
03
|
||||||
|
Choose anatomy
|
||||||
|
Escolher anatomia
|
||||||
|
only needed files
|
||||||
|
apenas arquivos necessários
|
||||||
04
|
04
|
||||||
Write what changes decisions
|
Write guidance
|
||||||
|
Escrever orientação
|
||||||
|
decisions, not trivia
|
||||||
|
decisões, não trivialidades
|
||||||
|
05
|
||||||
|
Validate
|
||||||
|
Validar
|
||||||
|
test real behavior
|
||||||
|
teste comportamento real
|
||||||
01
|
01
|
||||||
QUESTION
|
QUESTION
|
||||||
Start from repeated friction
|
Start from repeated friction
|
||||||
@@ -332,31 +396,88 @@ ARTIFACT
|
|||||||
A narrow capability and concrete examples.
|
A narrow capability and concrete examples.
|
||||||
PROOF
|
PROOF
|
||||||
Without the skill, agents repeatedly make the same avoidable mistake.
|
Without the skill, agents repeatedly make the same avoidable mistake.
|
||||||
|
OUTPUT / SKILL PACKAGE
|
||||||
|
SAÍDA / PACOTE DE SKILL
|
||||||
|
review-ui/
|
||||||
|
├── SKILL.md
|
||||||
|
├── agents/
|
||||||
|
│ └── openai.yaml
|
||||||
|
├── references/
|
||||||
|
│ └── accessibility.md
|
||||||
|
└── scripts/
|
||||||
|
└── verify.mjs
|
||||||
|
VALIDATE
|
||||||
|
VALIDAR
|
||||||
|
quick_validate.py ./review-ui
|
||||||
|
AFTER REAL USE
|
||||||
|
APÓS USO REAL
|
||||||
|
observe failure
|
||||||
|
→
|
||||||
|
sharpen one rule
|
||||||
|
→
|
||||||
|
retest behavior
|
||||||
|
→
|
||||||
|
keep it narrow
|
||||||
|
observar falha
|
||||||
|
→
|
||||||
|
refinar uma regra
|
||||||
|
→
|
||||||
|
retestar comportamento
|
||||||
|
→
|
||||||
|
manter estreita
|
||||||
Common skills
|
Common skills
|
||||||
Skills comuns
|
Skills comuns
|
||||||
choose behavior before model
|
choose behavior before model
|
||||||
escolha o comportamento antes do modelo
|
escolha o comportamento antes do modelo
|
||||||
COMMUNICATION STYLE
|
The field kit
|
||||||
caveman
|
O kit de campo
|
||||||
Use for routine status, handoffs, and technical summaries where speed matters. Short fragments make actions and evidence easy to scan.
|
Different jobs.
|
||||||
DIAGNOSTIC LOOP
|
Different
|
||||||
diagnosing-bugs
|
instincts.
|
||||||
Use for hard bugs, flakes, and regressions. First build a fast deterministic reproduction, then minimize, rank hypotheses, instrument, and fix the root cause.
|
Trabalhos diferentes.
|
||||||
SIMPLIFICATION INSTINCT
|
Instintos
|
||||||
|
diferentes.
|
||||||
|
A skill changes how an agent approaches work. Some shape communication. Others enforce
|
||||||
|
research, debugging, review, or completion discipline. Select one to inspect its
|
||||||
|
operating rule and verified source.
|
||||||
|
Uma skill muda como o agente aborda o trabalho. Algumas moldam a comunicação. Outras
|
||||||
|
impõem pesquisa, diagnóstico, revisão ou disciplina de conclusão. Selecione uma para
|
||||||
|
inspecionar sua regra operacional.
|
||||||
|
SIMPLIFY
|
||||||
|
SIMPLIFICAR
|
||||||
ponytail-lite
|
ponytail-lite
|
||||||
Use when a request invites frameworks, dependencies, abstractions, or speculative scaffolding. It checks reuse, standard library, and native platform features before adding code.
|
minimum code that holds
|
||||||
CONTEXT ECONOMY
|
código mínimo que funciona
|
||||||
token-saver
|
COMMUNICATE
|
||||||
Use around verbose tests, builds, Git output, and logs. Filtering preserves context for reasoning while retaining full failure output for recovery.
|
COMUNICAR
|
||||||
SOURCE DISCIPLINE
|
caveman
|
||||||
research
|
signal without filler
|
||||||
Use when APIs, standards, architecture facts, or current behavior must be verified. Capture findings in a cited note, prioritizing primary sources.
|
sinal sem excesso
|
||||||
COMPLETION DISCIPLINE
|
COMPLETE
|
||||||
|
CONCLUIR
|
||||||
unlazy
|
unlazy
|
||||||
Use for substantial autonomous builds, audits, and parallel work where quiet omissions are expensive. It turns “done” into runnable acceptance checks.
|
gates and evidence
|
||||||
INDEPENDENT REVIEW
|
gates e evidências
|
||||||
|
INVESTIGATE
|
||||||
|
INVESTIGAR
|
||||||
|
research
|
||||||
|
primary sources first
|
||||||
|
fontes primárias primeiro
|
||||||
|
DIAGNOSE
|
||||||
|
DIAGNOSTICAR
|
||||||
|
diagnosing-bugs
|
||||||
|
tight feedback loop
|
||||||
|
ciclo curto de feedback
|
||||||
|
REVIEW
|
||||||
|
REVISAR
|
||||||
code-review
|
code-review
|
||||||
Use on a branch or PR. One axis checks repository standards; another checks whether the change actually satisfies its originating specification.
|
standards × spec
|
||||||
|
padrões × especificação
|
||||||
|
ECONOMIZE
|
||||||
|
ECONOMIZAR
|
||||||
|
token-saver
|
||||||
|
compress noisy output
|
||||||
|
comprima saídas ruidosas
|
||||||
01
|
01
|
||||||
SIMPLIFICATION INSTINCT
|
SIMPLIFICATION INSTINCT
|
||||||
ponytail-lite
|
ponytail-lite
|
||||||
@@ -368,8 +489,34 @@ Date picker? Start with <input type="date">.
|
|||||||
WATCH OUT
|
WATCH OUT
|
||||||
Never simplify away security, accessibility, validation, or real edge cases.
|
Never simplify away security, accessibility, validation, or real edge cases.
|
||||||
GITHUB SOURCE ↗
|
GITHUB SOURCE ↗
|
||||||
|
ONE PRACTICAL LOADOUT
|
||||||
|
UM LOADOUT PRÁTICO
|
||||||
|
PLAN
|
||||||
|
unlazy
|
||||||
|
→
|
||||||
|
BUILD
|
||||||
|
ponytail-lite
|
||||||
|
→
|
||||||
|
DEBUG
|
||||||
|
diagnosing-bugs
|
||||||
|
→
|
||||||
|
REPORT
|
||||||
|
caveman
|
||||||
|
PLANEJAR
|
||||||
|
unlazy
|
||||||
|
→
|
||||||
|
CONSTRUIR
|
||||||
|
ponytail-lite
|
||||||
|
→
|
||||||
|
DIAGNOSTICAR
|
||||||
|
diagnosing-bugs
|
||||||
|
→
|
||||||
|
REPORTAR
|
||||||
|
caveman
|
||||||
INSTALL PACK
|
INSTALL PACK
|
||||||
|
PACOTE DE INSTALAÇÃO
|
||||||
Ask your coding agent to verify, install, and validate the skills.
|
Ask your coding agent to verify, install, and validate the skills.
|
||||||
|
Peça ao seu agente para verificar, instalar e validar as skills.
|
||||||
COPY
|
COPY
|
||||||
↗
|
↗
|
||||||
Inspect and install only these public agent skills. Pin the exact commits:
|
Inspect and install only these public agent skills. Pin the exact commits:
|
||||||
@@ -380,19 +527,61 @@ Inspect and install only these public agent skills. Pin the exact commits:
|
|||||||
- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — repository root
|
- aetox-skills/token-saver@8f21188bb043fad411f47e2e57f0365a83c13da7 — repository root
|
||||||
- anthropics/skills@53048666b05b4799081517d00e09e0a2dd688678 — skills/webapp-testing/
|
- 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.
|
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.
|
||||||
|
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.
|
||||||
Review every source before installation. Existing local skills must be preserved.
|
Review every source before installation. Existing local skills must be preserved.
|
||||||
|
Revise cada fonte antes da instalação. Skills locais existentes devem ser preservadas.
|
||||||
Hands-on
|
Hands-on
|
||||||
|
Prática
|
||||||
10 minutes / one missing feature
|
10 minutes / one missing feature
|
||||||
|
10 minutos / uma feature ausente
|
||||||
Tiny Tasks lab
|
Tiny Tasks lab
|
||||||
|
Laboratório Tiny Tasks
|
||||||
Same task.
|
Same task.
|
||||||
Better
|
Better
|
||||||
operating system.
|
operating system.
|
||||||
|
Mesma tarefa.
|
||||||
|
Melhor
|
||||||
|
sistema operacional.
|
||||||
Start with a deliberately incomplete static task board. Run one prompt as written,
|
Start with a deliberately incomplete static task board. Run one prompt as written,
|
||||||
reset, then run the skill-enabled version.
|
reset, then run the skill-enabled version. Compare diff size, verification evidence, and
|
||||||
|
unnecessary complexity.
|
||||||
|
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.
|
||||||
Open the starter →
|
Open the starter →
|
||||||
|
Abrir o projeto inicial →
|
||||||
|
Clone from Gitea →
|
||||||
|
Abrir o projeto inicial →
|
||||||
Open the rules lab →
|
Open the rules lab →
|
||||||
|
Abrir o projeto inicial →
|
||||||
|
Clone from Gitea →
|
||||||
|
Abrir o projeto inicial →
|
||||||
|
THE MISSING FEATURE
|
||||||
|
A FEATURE AUSENTE
|
||||||
|
Add All / Open / Done filters that survive reload and browser navigation.
|
||||||
|
Adicione filtros Todos / Abertos / Concluídos que sobrevivem reload e navegação.
|
||||||
|
STACK
|
||||||
|
HTML · CSS · JavaScript
|
||||||
|
DEPENDENCIES
|
||||||
|
none
|
||||||
|
FILES
|
||||||
|
3
|
||||||
|
STACK
|
||||||
|
HTML · CSS · JavaScript
|
||||||
|
DEPENDÊNCIAS
|
||||||
|
nenhuma
|
||||||
|
ARQUIVOS
|
||||||
|
3
|
||||||
RUN A
|
RUN A
|
||||||
Good prompt
|
Good prompt
|
||||||
|
Bom prompt
|
||||||
COPY
|
COPY
|
||||||
↗
|
↗
|
||||||
Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.
|
Work only in hands-on/starter. It is dependency-free HTML, CSS, and JavaScript.
|
||||||
@@ -407,9 +596,23 @@ Requirements:
|
|||||||
- add no dependencies and change no unrelated files
|
- add no dependencies and change no unrelated files
|
||||||
Verify app.js syntax and exercise every filter plus URL navigation.
|
Verify app.js syntax and exercise every filter plus URL navigation.
|
||||||
Return changed files, checks run, results, and remaining risk.
|
Return changed files, checks run, results, and remaining risk.
|
||||||
|
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.
|
||||||
Clear context · constraints · acceptance · evidence
|
Clear context · constraints · acceptance · evidence
|
||||||
|
Contexto claro · restrições · aceitação · evidência
|
||||||
RUN B
|
RUN B
|
||||||
Good prompt + skills
|
Good prompt + skills
|
||||||
|
Bom prompt + skills
|
||||||
COPY
|
COPY
|
||||||
↗
|
↗
|
||||||
Use $ponytail-lite and $webapp-testing.
|
Use $ponytail-lite and $webapp-testing.
|
||||||
@@ -423,25 +626,101 @@ Acceptance:
|
|||||||
- invalid status falls back safely to all
|
- invalid status falls back safely to all
|
||||||
- style remains consistent; unrelated files remain untouched
|
- style remains consistent; unrelated files remain untouched
|
||||||
Return the smallest working diff and concrete verification evidence.
|
Return the smallest working diff and concrete verification evidence.
|
||||||
|
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.
|
||||||
Same contract · explicit working methods · stronger proof
|
Same contract · explicit working methods · stronger proof
|
||||||
|
Mesmo contrato · métodos explícitos · prova mais forte
|
||||||
|
COMPARE THE RUNS
|
||||||
|
COMPARE AS EXECUÇÕES
|
||||||
|
01
|
||||||
|
Files changed
|
||||||
|
Arquivos alterados
|
||||||
|
02
|
||||||
|
New dependencies
|
||||||
|
Novas dependências
|
||||||
|
03
|
||||||
|
Checks actually run
|
||||||
|
Checks executados
|
||||||
|
04
|
||||||
|
Evidence returned
|
||||||
|
Evidências retornadas
|
||||||
THE HUMAN JOB
|
THE HUMAN JOB
|
||||||
O PAPEL HUMANO
|
O PAPEL HUMANO
|
||||||
The agent may be autonomous in execution. Intent, boundaries, and evidence remain yours.
|
The agent may be autonomous in execution. Intent, boundaries, and evidence remain yours.
|
||||||
O agente pode ser autônomo na execução. Intenção, limites e evidências continuam sendo seus.
|
O agente pode ser autônomo na execução. Intenção, limites e evidências continuam sendo
|
||||||
|
seus.
|
||||||
|
START HERE
|
||||||
|
COMECE AQUI
|
||||||
|
Begin with one agent and one skill. Add parallelism only when the tasks are truly
|
||||||
|
independent.
|
||||||
|
Comece com um agente e uma skill. Adicione paralelismo apenas quando as tarefas forem
|
||||||
|
realmente independentes.
|
||||||
Verification
|
Verification
|
||||||
run each gate separately
|
run each gate separately
|
||||||
|
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.
|
||||||
01 · STATIC
|
01 · STATIC
|
||||||
Lint and types
|
Lint and types
|
||||||
Format, lint, type-check. Fast and scoped to one file.
|
Format, lint, type-check. Fast and scoped to one file. Run on every save.
|
||||||
pnpm lint; echo "lint=$?"
|
pnpm lint; echo "lint=$?" pnpm typecheck; echo "typecheck=$?"
|
||||||
02 · BEHAVIOR
|
02 · BEHAVIOR
|
||||||
Unit and contract
|
Unit and contract
|
||||||
Tests that repeat. Run before claiming done.
|
Tests that repeat. Run before claiming done.
|
||||||
pnpm test; echo "test=$?"
|
pnpm test; echo "test=$?" cd services/api && go test ./...
|
||||||
03 · INTEGRATION
|
03 · INTEGRATION
|
||||||
Real UI and API
|
Real UI and API
|
||||||
Drive the actual UI, API, or browser.
|
Drive the actual UI, API, or browser. Slower and flakier — only this catches mobile
|
||||||
pnpm check:ui; echo "ui=$?"
|
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
|
||||||
|
.
|
||||||
|
Open the starter →
|
||||||
|
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.
|
||||||
|
Open the rules lab →
|
||||||
|
Open the rules lab →
|
||||||
|
Clone ↗
|
||||||
|
git.marcospaulo.dev.br/.../src/branch/pages/hands-on/rules
|
||||||
Keep learning
|
Keep learning
|
||||||
Continue aprendendo
|
Continue aprendendo
|
||||||
12 new readings + primary docs
|
12 new readings + primary docs
|
||||||
@@ -450,8 +729,26 @@ Go deeper with official documentation, production case studies, Medium, and prac
|
|||||||
workflows.
|
workflows.
|
||||||
Rules and enforcement case study →
|
Rules and enforcement case study →
|
||||||
Skills review desk →
|
Skills review desk →
|
||||||
|
Primary references →
|
||||||
12-part reading path →
|
12-part reading path →
|
||||||
Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes.
|
Aprofunde com documentação oficial, casos de produção, Medium e fluxos de praticantes.
|
||||||
Estudo de caso sobre regras e enforcement →
|
Estudo de caso sobre regras e enforcement →
|
||||||
Skills review desk →
|
Referências primárias →
|
||||||
Trilha com 12 leituras →
|
Trilha com 12 leituras →
|
||||||
|
Navigate by idea
|
||||||
|
short chapters / one system
|
||||||
|
Prefer a focused chapter? Start with the
|
||||||
|
route map
|
||||||
|
, then
|
||||||
|
jump directly to
|
||||||
|
models
|
||||||
|
,
|
||||||
|
agents and worktrees
|
||||||
|
,
|
||||||
|
skill creation
|
||||||
|
,
|
||||||
|
rules
|
||||||
|
, or
|
||||||
|
the
|
||||||
|
skills review desk
|
||||||
|
.
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ jobs:
|
|||||||
# back in once it can actually diff. See task 03's report.
|
# back in once it can actually diff. See task 03's report.
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
# Until the migration finishes, `dist/` holds only /summary/ and the two
|
# `dist/` now holds all ten routes, so the stub hazard that forced this to
|
||||||
# hands-on fixtures, while the live `pages` branch serves ten pages.
|
# manual dispatch is gone. It stays manual anyway: the step below is a
|
||||||
# Publishing on every push to main would take the site down to a stub, so
|
# force-push over the live `pages` branch, and making it fire on every push
|
||||||
# this job runs only when a human asks for it. Make it unconditional on
|
# to main means every merge republishes with no human in the loop. Flipping
|
||||||
# main again at task 20 (cutover), not before.
|
# it to `push` on main is a deliberate decision, not a leftover TODO.
|
||||||
if: github.event_name == 'workflow_dispatch' && inputs.publish
|
if: github.event_name == 'workflow_dispatch' && inputs.publish
|
||||||
needs: gate
|
needs: gate
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -9,21 +9,9 @@ vote-service
|
|||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
public/submitted-skills
|
public/submitted-skills
|
||||||
|
|
||||||
# Legacy site sources, slated for deletion at cutover (task 20). These are
|
# Unmigrated legacy sources, kept verbatim under `legacy/`. These are
|
||||||
# hand-written files with very long lines; prettier re-wraps them into hundreds
|
# hand-written files with very long lines; prettier re-wraps them into hundreds
|
||||||
# of changed lines the moment any agent stages one. Task 15 touched app.js to
|
# of changed lines the moment any agent stages one. Task 15 touched the old
|
||||||
# add four lines and produced an 829-line diff. verify.mjs asserts substrings
|
# app.js to add four lines and produced an 829-line diff. A reformat here is
|
||||||
# against several of these, so a reformat is churn at best and a broken
|
# churn at best.
|
||||||
# assertion at worst.
|
/legacy/
|
||||||
#
|
|
||||||
# Root-anchored on purpose: a bare `rules` would also swallow .agents/rules/,
|
|
||||||
# whose markdown we do want formatted.
|
|
||||||
/app.js
|
|
||||||
/styles.css
|
|
||||||
/landing.css
|
|
||||||
/chapters.css
|
|
||||||
/responsive.css
|
|
||||||
/skills-review/
|
|
||||||
/rules/
|
|
||||||
/skills/
|
|
||||||
/full-guide/
|
|
||||||
|
|||||||
@@ -7,23 +7,16 @@ public/submitted-skills
|
|||||||
skill-reviews
|
skill-reviews
|
||||||
vote-service
|
vote-service
|
||||||
|
|
||||||
# Legacy site sources, slated for deletion at cutover (task 20). Same list and
|
# Unmigrated legacy stylesheets, kept verbatim under `legacy/`. Same list and
|
||||||
# same reasoning as .prettierignore: these are minified, single-line
|
# same reasoning as .prettierignore: these are minified, single-line
|
||||||
# stylesheets. stylelint's `declaration-block-single-line-max-declarations`
|
# stylesheets. stylelint's `declaration-block-single-line-max-declarations`
|
||||||
# fires once per rule in them — ~180 errors for `styles.css` alone — so staging
|
# fires once per rule in them — ~180 errors for `guide.css` alone — so staging
|
||||||
# one to change a single declaration blocks the commit outright. The rule is
|
# one to change a single declaration blocks the commit outright. The rule is
|
||||||
# about hand-written source readability and says nothing useful about minified
|
# about hand-written source readability and says nothing useful about minified
|
||||||
# output that is about to be deleted.
|
# legacy output. Migrating one of these into `src/` means bringing it up to the
|
||||||
|
# design system in the same change, at which point it gets linted like any
|
||||||
|
# other source file.
|
||||||
#
|
#
|
||||||
# `public/fonts/fonts.css` is deliberately NOT here: it is new, hand-written,
|
# `public/fonts/fonts.css` is deliberately NOT here: it is new, hand-written,
|
||||||
# and must stay linted.
|
# and must stay linted.
|
||||||
#
|
/legacy/
|
||||||
# Root-anchored on purpose: a bare `rules` would also swallow .agents/rules/.
|
|
||||||
/styles.css
|
|
||||||
/landing.css
|
|
||||||
/chapters.css
|
|
||||||
/responsive.css
|
|
||||||
/skills-review/
|
|
||||||
/rules/
|
|
||||||
/skills/
|
|
||||||
/full-guide/
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
"hands-on/**",
|
"hands-on/**",
|
||||||
"public/hands-on/**",
|
"public/hands-on/**",
|
||||||
"submitted-skills/**",
|
"submitted-skills/**",
|
||||||
"skill-reviews/**",
|
"skill-reviews/**"
|
||||||
"vote-service/**"
|
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"custom-property-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
"custom-property-pattern": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
||||||
|
|||||||
@@ -12,48 +12,48 @@ rules, and verification.** It is published as a static site on a self-hosted
|
|||||||
Gitea Pages Server, and it doubles as its own teaching artifact: the hands-on
|
Gitea Pages Server, and it doubles as its own teaching artifact: the hands-on
|
||||||
labs are dependency-free HTML/CSS/JS that workshop attendees point an agent at.
|
labs are dependency-free HTML/CSS/JS that workshop attendees point an agent at.
|
||||||
|
|
||||||
- **Current stack**: hand-written HTML + CSS + ES modules, no build step, no
|
- **Stack**: Astro, static output, no runtime dependencies. The migration
|
||||||
dependencies
|
recorded in [`plans/astro-refactor/`](plans/astro-refactor/README.md) is
|
||||||
- **Target stack**: Astro (see
|
complete; the hand-written pages it replaced are gone. What remains unmigrated
|
||||||
[`plans/astro-refactor/`](plans/astro-refactor/README.md)) — migration in
|
is the editorial CSS and the review-desk modules under `legacy/`, still
|
||||||
progress
|
imported by the pages that need them.
|
||||||
- **Languages**: English and Brazilian Portuguese, toggled client-side
|
- **Languages**: English and Brazilian Portuguese, toggled client-side
|
||||||
- **Companion service**: `vote-service/` (Go + Kubernetes) — separate lifecycle,
|
- **Companion service**: a Go + Kubernetes vote API, reached over
|
||||||
see its own README
|
`window.SKILLS_REVIEW_VOTE_API`. Its source is no longer in this repository
|
||||||
|
|
||||||
## Essential commands
|
## Essential commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run verify # content + interaction contracts (scripts/verify.mjs) — the gate
|
pnpm run dev # http://localhost:4321/ai-for-dummies/
|
||||||
|
pnpm run build # writes dist/ — every check below reads it
|
||||||
|
bash .agents/scripts/gate.sh # the full gate: check, build, verify, audit, tokens
|
||||||
|
pnpm run verify # content + interaction contracts (scripts/verify.mjs)
|
||||||
node scripts/audit-ui.mjs # responsive / no-external-dependency audit
|
node scripts/audit-ui.mjs # responsive / no-external-dependency audit
|
||||||
node scripts/build-skill-review.mjs # regenerate skill-reviews/improved/ from src/content/reviews/
|
node scripts/build-skill-review.mjs # regenerate skill-reviews/improved/ from src/content/reviews/
|
||||||
pnpm run serve # python3 -m http.server 4173
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`pnpm run verify` is not a formality. It is a set of ~42 string-token assertions
|
`pnpm run verify` is not a formality. It is a set of 84 string-token assertions
|
||||||
that pin the site's real content and interactions. **A refactor that "passes" by
|
that pin the site's real content and interactions, read from the built output.
|
||||||
deleting assertions has failed.** See
|
**A refactor that "passes" by deleting assertions has failed.** See
|
||||||
[`.agents/context/verification.md`](.agents/context/verification.md).
|
[`.agents/context/verification.md`](.agents/context/verification.md).
|
||||||
|
|
||||||
## Publishing
|
## Publishing
|
||||||
|
|
||||||
`main` is the source of truth. The `pages` branch is what the Gitea Pages Server
|
`main` is the source of truth. The `pages` branch is what the Gitea Pages Server
|
||||||
actually serves, and its tree must end up identical to `main`'s. The full
|
actually serves, and it now carries **build output**, not a copy of `main`'s
|
||||||
procedure — including why `merge --ff-only` does _not_ work here — is in
|
tree. The publish job force-pushes `dist/` over it. The full procedure is in
|
||||||
[`docs/operations-guide.md`](docs/operations-guide.md).
|
[`docs/operations-guide.md`](docs/operations-guide.md); read
|
||||||
|
[`.agents/context/publishing.md`](.agents/context/publishing.md) before changing
|
||||||
Adding a build step changes this contract. Read
|
it.
|
||||||
[`.agents/context/publishing.md`](.agents/context/publishing.md) before doing
|
|
||||||
so.
|
|
||||||
|
|
||||||
## Never touch
|
## Never touch
|
||||||
|
|
||||||
- `hands-on/starter/` and `hands-on/rules/` — **lab fixtures.** The exercise
|
- `public/hands-on/starter/` and `public/hands-on/rules/` — **lab fixtures.**
|
||||||
_is_ that they are dependency-free vanilla HTML/CSS/JS an attendee can hand to
|
The exercise _is_ that they are dependency-free vanilla HTML/CSS/JS an
|
||||||
an agent. Componentizing them destroys the lesson. They ship as static assets.
|
attendee can hand to an agent. Componentizing them destroys the lesson. They
|
||||||
|
ship as static assets.
|
||||||
- `submitted-skills/` — other people's submitted work, reproduced verbatim
|
- `submitted-skills/` — other people's submitted work, reproduced verbatim
|
||||||
- `skill-reviews/improved/` — generated; edit `src/content/reviews/*.md` instead
|
- `skill-reviews/improved/` — generated; edit `src/content/reviews/*.md` instead
|
||||||
- `vote-service/` — separate deploy lifecycle; do not fold into the site build
|
|
||||||
- `dist/`, `node_modules/` — build output, never committed
|
- `dist/`, `node_modules/` — build output, never committed
|
||||||
- `pnpm-lock.yaml` — **committed, but never hand-edited.** Change it only as a
|
- `pnpm-lock.yaml` — **committed, but never hand-edited.** Change it only as a
|
||||||
side effect of `pnpm install`. Every worktree spins up with
|
side effect of `pnpm install`. Every worktree spins up with
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
# Gates: review desk privacy and improved-draft audit
|
# Gates: review desk privacy and improved-draft audit
|
||||||
|
|
||||||
OWNS: skills-review/**, submitted-skills/Anonymous Operational Submission/**,
|
OWNS: src/pages/skills-review.astro, src/components/blocks/{ReviewDetail,
|
||||||
|
ChangeLens,VoteWidget,PreviewPane,FileTabs}.astro, legacy/skills-review/**,
|
||||||
|
submitted-skills/Anonymous Operational Submission/**,
|
||||||
skill-reviews/improved/ndo-repro/**, scripts/verify.mjs
|
skill-reviews/improved/ndo-repro/**, scripts/verify.mjs
|
||||||
|
|
||||||
|
The gate commands below now read `dist/`; run `pnpm run build` before them.
|
||||||
|
|
||||||
Scope: Redact the operational submission's identity and URLs from the published
|
Scope: Redact the operational submission's identity and URLs from the published
|
||||||
review desk, keep package files usable in either preview mode, and explain each
|
review desk, keep package files usable in either preview mode, and explain each
|
||||||
improved draft as a concrete diff.
|
improved draft as a concrete diff.
|
||||||
|
|||||||
@@ -15,47 +15,58 @@ skill links to a pinned source with an approval-first installation prompt.
|
|||||||
|
|
||||||
## Run locally
|
## Run locally
|
||||||
|
|
||||||
This is a dependency-free static site:
|
This is an Astro static site. It builds to `dist/` and ships no runtime
|
||||||
|
dependencies.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 -m http.server 4173
|
pnpm install
|
||||||
|
pnpm run dev # http://localhost:4321/ai-for-dummies/
|
||||||
|
pnpm run build # writes dist/
|
||||||
|
pnpm run preview # serves the built output
|
||||||
```
|
```
|
||||||
|
|
||||||
Then open <http://localhost:4173>.
|
|
||||||
|
|
||||||
Verify the content and interaction contracts with:
|
Verify the content and interaction contracts with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run verify
|
pnpm run verify # reads dist/, so build first
|
||||||
|
```
|
||||||
|
|
||||||
|
The full gate — `astro check`, `astro build`, `verify.mjs`, `audit-ui.mjs`,
|
||||||
|
`check-tokens.mjs`, and the assertion-count floor — runs as:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash .agents/scripts/gate.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project structure
|
## Project structure
|
||||||
|
|
||||||
- `index.html` — default route map and focused chapter navigation
|
- `src/pages/` — one file per route: the landing route map, the complete
|
||||||
- `full-guide/` — the complete bilingual presentation, with responsive audit
|
bilingual `full-guide`, the chapter pages, `rules`, `skills`, and
|
||||||
overrides
|
`skills-review`
|
||||||
- `styles.css` / `app.js` — editorial visual system and bilingual field-guide
|
- `src/components/` — blocks and islands; the interactive diagrams, selectors,
|
||||||
interactions
|
and the language toggle
|
||||||
- `responsive.css` — interactive diagrams and Full HD-to-4K adaptations
|
- `src/content/` — the content collections every page renders from
|
||||||
|
- `src/styles/tokens.css` — the design tokens
|
||||||
|
- `legacy/` — the editorial visual system and the review-desk modules, not yet
|
||||||
|
migrated into components. Still imported by the pages that need them; see
|
||||||
|
`.agents/context/architecture.md`
|
||||||
|
- `public/` — assets copied to the site root verbatim: fonts, the hands-on labs,
|
||||||
|
and `submitted-skills/`
|
||||||
- `docs/references/` — bundled research sources and notes
|
- `docs/references/` — bundled research sources and notes
|
||||||
- `docs/operations-guide.md` — canonical SilverBullet operations and skills
|
- `docs/operations-guide.md` — canonical SilverBullet operations and skills
|
||||||
guide
|
guide
|
||||||
- `hands-on/starter/` — dependency-free Tiny Tasks exercise
|
- `public/hands-on/starter/` — dependency-free Tiny Tasks exercise
|
||||||
- `hands-on/rules/` — dependency-free Guardrails lab; toggles rule sources into
|
- `public/hands-on/rules/` — dependency-free Guardrails lab; toggles rule
|
||||||
the prompt
|
sources into the prompt
|
||||||
- `rules/` — bilingual case study of skills, CLI ratchets, Husky, and PR review
|
- `skills/` — reusable design and rules-case-study skills
|
||||||
- `skills/` — reusable design and rules-case-study skills, plus an interactive
|
|
||||||
package anatomy explorer
|
|
||||||
- `skills-review/` — static review desk for submitted skills; its reader vote
|
|
||||||
widget calls the separate `vote-service`
|
|
||||||
- `vote-service/` — small Go API + Kubernetes manifests backing the
|
|
||||||
skills-review vote widget (see `vote-service/README.md`)
|
|
||||||
- `GATES.md` — acceptance ledger for the project
|
- `GATES.md` — acceptance ledger for the project
|
||||||
|
|
||||||
## Publishing
|
## Publishing
|
||||||
|
|
||||||
The Gitea instance has a Pages Server configured to publish a repository’s
|
The Gitea instance has a Pages Server configured to publish a repository’s
|
||||||
`pages` branch under `pages.marcospaulo.dev.br`. The intended site address is:
|
`pages` branch under `pages.marcospaulo.dev.br`. `pages` now carries the
|
||||||
|
**built** site — the contents of `dist/` — not a copy of `main`. The intended
|
||||||
|
site address is:
|
||||||
|
|
||||||
<https://netcracker.pages.marcospaulo.dev.br/ai-for-dummies/>
|
<https://netcracker.pages.marcospaulo.dev.br/ai-for-dummies/>
|
||||||
|
|
||||||
@@ -70,12 +81,11 @@ skill workflow, see [docs/operations-guide.md](docs/operations-guide.md).
|
|||||||
## Reader voting on the skills-review desk
|
## Reader voting on the skills-review desk
|
||||||
|
|
||||||
`skills-review/` is static, so its "which draft would you ship?" vote widget
|
`skills-review/` is static, so its "which draft would you ship?" vote widget
|
||||||
calls a separate stateful service — `vote-service/`, a small Go API on its own
|
calls a separate stateful service — a small Go API on its own pod, one vote per
|
||||||
pod, one vote per visitor enforced server-side by IP (a MAC address is never
|
visitor enforced server-side by IP (a MAC address is never visible to a server
|
||||||
visible to a server across the internet, so it cannot be used). See
|
across the internet, so it cannot be used). Its source no longer lives in this
|
||||||
[vote-service/README.md](vote-service/README.md) for the API, the anti-abuse
|
repository; the deployed service is unchanged. `src/pages/skills-review.astro`
|
||||||
design, and the build/push/deploy steps; `skills-review/index.html` sets
|
sets `window.SKILLS_REVIEW_VOTE_API` to point at it.
|
||||||
`window.SKILLS_REVIEW_VOTE_API` to point at it once deployed.
|
|
||||||
|
|
||||||
## Research
|
## Research
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<!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>
|
|
||||||