refactor: retire the hand-written site
Deletes the pre-Astro pages, scripts, and stylesheets that the migration replaced, and moves the ones it did not replace out of the way. Deleted (32 files): app.js, responsive.css, landing.css, rules/app.js, rules/styles.css, skills/app.js, the ten route index.html files, and the root hands-on/ copy, which is byte-identical to public/hands-on/ -- the one the build actually ships. Moved to legacy/ (12 files): styles.css, full-guide/audit.css, chapters.css, skills/styles.css, skills-review/styles.css, skills-review/change-lens.css, and the skills-review/app.js module graph. These are not dead. The Astro pages import them and the build fails without them, which the plan had not accounted for. They go to legacy/ rather than src/ because check-tokens.mjs sweeps src, and these files are full of raw hex and unnamed breakpoints: moving one into src/ should mean migrating it to tokens in the same change, not adding a scan exclusion. The prettier, stylelint, and eslint ignore lists that already named these files at their old paths now name legacy/ instead. verify.mjs no longer reads app.js. The 102 Portuguese strings were extracted from its translations.pt object before deletion into .agents/snapshots/full-guide-pt.json -- a legacy capture, not a snapshot of the Astro build, so the assertion still compares against an independent source. The brace-matching helper's assertion is replaced by one that rejects an empty snapshot entry, without which trimming the snapshot would make the presence check pass vacuously. Count stays at 84. audit-ui.mjs reads the ten pages from dist/ and resolves Astro's base-absolute hrefs against it. Before deleting anything, rendered-text-diff was run across all ten routes plus both Portuguese pages: every one at parity, 0 missing and 0 extra. That comparison is not repeatable once the legacy files are gone. computed-style-diff on /full-guide/ stays at 32 differences, so the moves are style-neutral. Docs updated to match: README, AGENTS.md, GATES.md, the architecture context, the operations guide's lab instructions, and the three skills that told you to serve the vanilla site. Publishing is not part of this commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,124 +0,0 @@
|
||||
// Five rule sources lifted from netcracker/interview.
|
||||
// Toggling a rule injects its body into the ruled prompt.
|
||||
const RULES = [
|
||||
{
|
||||
id: 'agents',
|
||||
name: 'AGENTS.md',
|
||||
kind: 'Repo-wide instruction',
|
||||
path: 'AGENTS.md',
|
||||
en: 'Read AGENTS.md before touching this repo. Stack: pnpm + turbo monorepo, Go API, Next.js apps. Gates run from the monorepo root: pnpm lint, typecheck, test.',
|
||||
pt: 'Leia AGENTS.md antes de tocar neste repo. Stack: pnpm + turbo monorepo, API em Go, apps Next.js. Gates rodam da raiz: pnpm lint, typecheck, test.'
|
||||
},
|
||||
{
|
||||
id: 'skill',
|
||||
name: 'gate-discipline skill',
|
||||
kind: 'Skill body',
|
||||
path: '.agents/skills/gate-discipline/SKILL.md',
|
||||
en: 'Skill `gate-discipline`: every gate runs separately with `$?`. No `| tail`. `TURBO_FORCE=true` if a pass looks too cheap. Generated code is regenerated, never hand-edited.',
|
||||
pt: 'Skill `gate-discipline`: cada gate roda separado com `$?`. Nada de `| tail`. `TURBO_FORCE=true` se o passar for bom demais. Código gerado se regenera, nunca se edita.'
|
||||
},
|
||||
{
|
||||
id: 'husky',
|
||||
name: 'Husky pre-commit',
|
||||
kind: 'Git hook (commit time)',
|
||||
path: '.husky/pre-commit',
|
||||
en: 'Pre-commit runs `pnpm exec lint-staged`, then `node scripts/check-ui-contract.mjs` (UI ratchet), then commitlint. Counts in the baseline may only go DOWN.',
|
||||
pt: 'Pre-commit roda `pnpm exec lint-staged`, depois `node scripts/check-ui-contract.mjs` (ratchet de UI), depois commitlint. Contadores do baseline só podem DIMINUIR.'
|
||||
},
|
||||
{
|
||||
id: 'enforcer',
|
||||
name: 'check-ui-contract.mjs',
|
||||
kind: 'Custom enforcer (CLI)',
|
||||
path: 'scripts/check-ui-contract.mjs',
|
||||
en: 'Enforcer scans for raw <button>, silent catches, pages without h1, hardcoded colors, duplicated components. Fails when a count goes UP. Fix = `--accept` to re-baseline lower.',
|
||||
pt: 'Enforcer varre <button> cru, catch silencioso, páginas sem h1, cores hardcoded, componentes duplicados. Falha quando contador SOBE. Corrigir = `--accept` para re-baseline menor.'
|
||||
},
|
||||
{
|
||||
id: 'commitlint',
|
||||
name: 'commitlint',
|
||||
kind: 'Commit-msg linter',
|
||||
path: 'commitlint.config.cjs',
|
||||
en: 'Commitlint enforces Conventional Commits. Format: `<type>(<scope>): <subject>`. Types: feat, fix, docs, refactor, test, chore, build, ci, perf, style.',
|
||||
pt: 'Commitlint enforça Commits Convencionais. Formato: `<tipo>(<escopo>): <assunto>`. Tipos: feat, fix, docs, refactor, test, chore, build, ci, perf, style.'
|
||||
}
|
||||
];
|
||||
|
||||
// The task we're asking the agent to perform.
|
||||
const TASK = {
|
||||
en: { goal: 'Refactor the `getUserById` endpoint to return 404 instead of throwing.', ctx: 'services/api/internal/user/handler.go. No DB schema change.' },
|
||||
pt: { goal: 'Refatorar o endpoint `getUserById` para retornar 404 em vez de lançar exceção.', ctx: 'services/api/internal/user/handler.go. Sem mudança de schema.' }
|
||||
};
|
||||
|
||||
const NAIVE = {
|
||||
en: `Task: ${TASK.en.goal}\nFile: ${TASK.en.ctx}\nPlease make the change and tell me when done.`,
|
||||
pt: `Tarefa: ${TASK.pt.goal}\nArquivo: ${TASK.pt.ctx}\nFaça a mudança e me avise quando terminar.`
|
||||
};
|
||||
|
||||
const state = { enabled: new Set(['agents']), lang: 'en' };
|
||||
|
||||
const ruleList = document.querySelector('#rule-list');
|
||||
const ruleCount = document.querySelector('#rule-count');
|
||||
const promptNaive = document.querySelector('#prompt-naive');
|
||||
const promptRuled = document.querySelector('#prompt-ruled');
|
||||
const copyBtn = document.querySelector('#copy-btn');
|
||||
const langButtons = document.querySelectorAll('.lang-switch button');
|
||||
|
||||
function renderRules() {
|
||||
ruleList.innerHTML = RULES.map((rule) => `
|
||||
<article class="rule" data-id="${rule.id}">
|
||||
<div>
|
||||
<h3>${rule.name}</h3>
|
||||
<p>${rule.kind} <code>${rule.path}</code></p>
|
||||
</div>
|
||||
<span class="rule-tag">${rule.id}</span>
|
||||
<button class="toggle" type="button" data-rule="${rule.id}" aria-pressed="${state.enabled.has(rule.id)}" aria-label="Toggle ${rule.name}"></button>
|
||||
</article>
|
||||
`).join('');
|
||||
ruleCount.textContent = `${state.enabled.size} / ${RULES.length} active`;
|
||||
}
|
||||
|
||||
function renderPrompts() {
|
||||
const lang = state.lang;
|
||||
promptNaive.textContent = NAIVE[lang];
|
||||
|
||||
const active = RULES.filter((r) => state.enabled.has(r.id));
|
||||
const blocks = active.map((r) => `# ${r.name} (${r.path})\n${r[lang]}`).join('\n\n');
|
||||
const head = `Task: ${TASK[lang].goal}\nFile: ${TASK[lang].ctx}`;
|
||||
const tail = lang === 'en'
|
||||
? '\n\nRun each gate separately and print $? before claiming done.'
|
||||
: '\n\nRode cada gate separado e imprima $? antes de dizer que terminou.';
|
||||
promptRuled.textContent = blocks ? `${head}\n\n${blocks}${tail}` : head + tail;
|
||||
}
|
||||
|
||||
function bind() {
|
||||
ruleList.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.toggle');
|
||||
if (!btn) return;
|
||||
const id = btn.dataset.rule;
|
||||
if (state.enabled.has(id)) state.enabled.delete(id); else state.enabled.add(id);
|
||||
btn.setAttribute('aria-pressed', String(state.enabled.has(id)));
|
||||
ruleCount.textContent = `${state.enabled.size} / ${RULES.length} active`;
|
||||
renderPrompts();
|
||||
});
|
||||
|
||||
langButtons.forEach((btn) => btn.addEventListener('click', () => {
|
||||
state.lang = btn.dataset.lang;
|
||||
langButtons.forEach((b) => b.setAttribute('aria-pressed', String(b === btn)));
|
||||
renderPrompts();
|
||||
}));
|
||||
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(promptRuled.textContent);
|
||||
copyBtn.dataset.copied = 'true';
|
||||
copyBtn.textContent = 'Copied';
|
||||
setTimeout(() => { copyBtn.dataset.copied = 'false'; copyBtn.textContent = 'Copy ruled prompt'; }, 1400);
|
||||
} catch {
|
||||
copyBtn.textContent = 'Copy failed — select and copy manually';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderRules();
|
||||
renderPrompts();
|
||||
bind();
|
||||
Reference in New Issue
Block a user