diff --git a/README.md b/README.md index 32a7944..8bc8967 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ npm run verify - `docs/references/` — bundled research sources and notes - `docs/operations-guide.md` — canonical SilverBullet operations and skills guide - `hands-on/starter/` — dependency-free Tiny Tasks exercise +- `hands-on/rules/` — dependency-free Guardrails lab; toggles rule sources into the prompt +- `rules/` — bilingual case study of skills, CLI ratchets, Husky, and PR review - `GATES.md` — acceptance ledger for the project ## Publishing @@ -63,3 +65,12 @@ Codex, and Git documentation. The [additional reading path](docs/references/addi bundles 12 verified articles and guides, including Medium and practitioner sources. See [model routing](docs/references/model-routing.md) for current provider controls and [verified skill sources](docs/references/skill-sources.md) for commit-pinned provenance. + +## Rules and enforcement case study + +Open `/rules/` for a concise walkthrough grounded in the +`netcracker/interview` repository. It shows how `AGENTS.md`, project-local +skills, machine-readable repo ledgers, a UI contract ratchet, lint-staged, +Husky, commitlint, specialist verifier agents, and PR review reinforce one +another. Every example links to its source file in Gitea, and the page includes +a copy-ready prompt for mapping the same layers in another repository. diff --git a/docs/operations-guide.md b/docs/operations-guide.md index 20ec971..80e62bc 100644 --- a/docs/operations-guide.md +++ b/docs/operations-guide.md @@ -381,6 +381,45 @@ Compare: | Evidence | Did the final response name checks and results? | | Complexity | Is the solution proportionate to three tasks and three filters? | +### Hands-on rules lab + +A second lab at `hands-on/rules/` mirrors the starter's visual system and runs +the same exercise against rule sources instead of feature work. It lists five +toggleable rule sources — `AGENTS.md`, the `gate-discipline` skill body, the +Husky `pre-commit` hook, the `check-ui-contract.mjs` enforcer, and +`commitlint` — and rebuilds the **ruled** prompt live as each toggle flips. + +Run it: + +```bash +python3 -m http.server 4173 +``` + +Open [http://localhost:4173/hands-on/rules/](http://localhost:4173/hands-on/rules/). +Compare the **naive** and **ruled** prompt panels. Toggle rules off to shrink +the prompt; toggle them on to widen the leash. Copy the final prompt and run +it against a real coding agent — the lab is deliberately short so the spend +stays bounded. + +## Rules and enforcement case study + +The separate `/rules/` page uses `netcracker/interview` as a concrete example +of repository-level control. Its interactive pipeline shows five layers: + +1. `AGENTS.md` gives every agent the same product and toolchain context. +2. `.agents/skills/` loads narrow procedures for frontend, Go API, gates, + parallel work, repository ledgers, issues, skill writing, and technical debt. +3. `pnpm check:ui` compares violations with a baseline that may only decrease. +4. Husky runs lint-staged and the UI ratchet before commit; commitlint enforces + Conventional Commit messages. +5. `.pr-review.json` supplies repository-specific policy to the AI reviewer, + while the verifier agent reruns gates independently before merge. + +The page links directly to each implementation in Gitea and includes a +copy-ready, read-only prompt for mapping the same enforcement layers in another +repository. Update `rules/index.html`, `rules/app.js`, and `rules/styles.css` +together when the underlying interview workflow changes. + ## Troubleshooting | Symptom | Check | Fix | diff --git a/hands-on/rules/README.md b/hands-on/rules/README.md new file mode 100644 index 0000000..fac31d9 --- /dev/null +++ b/hands-on/rules/README.md @@ -0,0 +1,28 @@ +# Hands-on · Rules + +A tiny, zero-dependency demo showing how rule sources reshape the same prompt. + +## Run + +Open `index.html` directly. No build, no server, no `npm install`. + +## What it shows + +- Five rule sources, taken from a real monorepo (`netcracker/interview`): + - `AGENTS.md` — repo-wide instruction file. + - `.agents/skills/gate-discipline/SKILL.md` — skill body loaded on demand. + - `.husky/pre-commit` — git hook that runs other enforcers. + - `scripts/check-ui-contract.mjs` — custom CLI enforcer (ratchet). + - `commitlint.config.cjs` — commit-msg linter. +- Each rule has an on/off switch. Toggling a rule prepends its body to the **ruled** prompt. +- EN ↔ PT toggle keeps both languages useful. +- "Copy ruled prompt" copies the current ruled-prompt text to clipboard. +- Responsive on mobile, Full HD, and 4K (one column under 720 px). + +## Mirror of `/hands-on/starter` + +Same visual system (`--paper`, `--ink`, `--blue`, `--gold`), same vanilla JS pattern, same single-file layout. Drop-in alongside the starter page under `hands-on/rules/`. + +## Token budget + +Page weight: ~5 KB total, no framework, no fetch. diff --git a/hands-on/rules/app.js b/hands-on/rules/app.js new file mode 100644 index 0000000..06f1904 --- /dev/null +++ b/hands-on/rules/app.js @@ -0,0 +1,124 @@ +// 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 + + `).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(); diff --git a/hands-on/rules/index.html b/hands-on/rules/index.html new file mode 100644 index 0000000..05b6a1f --- /dev/null +++ b/hands-on/rules/index.html @@ -0,0 +1,43 @@ + + + + + + Guardrails — Hands-on Rules + + + +
+
+
HANDS-ON / RULES

Guardrails

+

Toggle rules. Watch the prompt change. Same task, different leash.

+
+ +
+

Rule sources

0 / 5 active
+
+
+ +
+

Prompt diff

+
+ + +
+
+
+
+
NAIVE

Vanilla ask

+

+        
+
+
RULED

With guardrails

+

+          
+        
+
+
+
+ + + diff --git a/hands-on/rules/styles.css b/hands-on/rules/styles.css new file mode 100644 index 0000000..898f13e --- /dev/null +++ b/hands-on/rules/styles.css @@ -0,0 +1 @@ +:root{--paper:#f4f3ef;--ink:#173044;--muted:#687d8c;--blue:#5683a1;--gold:#efc86d;--line:#d5dde1}*{box-sizing:border-box}body{margin:0;min-width:320px;background:var(--paper);color:var(--ink);font-family:Arial,sans-serif}main{width:min(880px,calc(100% - 40px));margin:0 auto;padding:70px 0}header,.section-head{display:flex;justify-content:space-between;gap:30px;align-items:end}header{padding-bottom:50px;border-bottom:1px solid var(--line)}header span,.prompt-card>header span{font:10px monospace;letter-spacing:.08em}h1{margin:12px 0 0;font-size:clamp(48px,10vw,100px);letter-spacing:-.08em}header p{max-width:220px;color:var(--muted);line-height:1.5}section{padding-top:45px}h2{font-size:24px}.section-head>span{color:var(--blue);font:11px monospace}.rule-list,.prompt-grid{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}.rule{display:grid;grid-template-columns:1fr auto auto;gap:20px;padding:22px;background:var(--paper);align-items:center}.rule h3{margin:0 0 6px;font-size:17px}.rule p{margin:0;color:var(--muted);font-size:13px;line-height:1.45}.rule code{font:11px ui-monospace,monospace;color:var(--ink);background:var(--paper);padding:1px 5px;border:1px solid var(--line)}.rule-tag{font:10px monospace;letter-spacing:.08em;color:var(--muted);text-transform:uppercase}.toggle{appearance:none;width:44px;height:24px;border:1px solid var(--line);background:var(--paper);border-radius:12px;position:relative;cursor:pointer;transition:background .15s ease}.toggle::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;background:var(--ink);border-radius:50%;transition:transform .15s ease,background .15s ease}.toggle[aria-pressed="true"]{background:var(--blue);border-color:var(--blue)}.toggle[aria-pressed="true"]::after{transform:translateX(20px);background:var(--gold)}.prompt-grid{grid-template-columns:1fr 1fr;margin-top:18px}.prompt-card{background:var(--paper);padding:22px;display:flex;flex-direction:column;gap:14px}.prompt-card>header{display:flex;justify-content:space-between;align-items:end;padding-bottom:0;border-bottom:0}.prompt-card h3{margin:0;font-size:17px}.prompt-card pre{margin:0;font:12px ui-monospace,monospace;white-space:pre-wrap;word-break:break-word;color:var(--ink);background:var(--paper);border:1px solid var(--line);padding:14px;min-height:160px;line-height:1.5}#copy-btn{align-self:flex-start;appearance:none;border:1px solid var(--ink);background:var(--ink);color:var(--paper);font:11px monospace;letter-spacing:.08em;padding:9px 14px;cursor:pointer;text-transform:uppercase}#copy-btn[data-copied="true"]{background:var(--blue);border-color:var(--blue)}.lang-switch{display:flex;gap:1px;border:1px solid var(--line)}.lang-switch button{appearance:none;border:0;background:var(--paper);color:var(--muted);font:11px monospace;letter-spacing:.08em;padding:6px 10px;cursor:pointer;text-transform:uppercase}.lang-switch button[aria-pressed="true"]{background:var(--ink);color:var(--paper)}@media(max-width:720px){.prompt-grid{grid-template-columns:1fr}.rule{grid-template-columns:1fr}.rule-tag{display:none}}@media(max-width:560px){header{display:block}header p{margin-top:24px}.lang-switch{flex:1}.lang-switch button{flex:1}} diff --git a/index.html b/index.html index 0f43114..f6a8ef1 100644 --- a/index.html +++ b/index.html @@ -25,8 +25,8 @@

Skills

Write the right way
once.

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.

01 / trigger clearly02 / load detail on demand03 / return evidence
SKILL PACKAGE
name: review-ui · check focus, mobile, reduced motion · run verification · return evidence
Create a skillrepeatable pain → reusable judgment

The skill forge

Teach the decision.
Keep the context light.

Do not package everything you know. Capture the non-obvious choices that repeatedly improve an outcome, then prove the skill changes behavior.

AFTER REAL USE
observe failuresharpen one ruleretest behaviorkeep it narrow
Common skillschoose behavior before model

The field kit

Different jobs.
Different instincts.

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.

ONE PRACTICAL LOADOUT
PLAN unlazy BUILD ponytail-lite DEBUG diagnosing-bugs REPORT caveman
INSTALL PACKAsk your coding agent to verify, install, and validate the skills.
-
Hands-on10 minutes / one missing feature

Tiny Tasks lab

Same task.
Better operating system.

Start with a deliberately incomplete static task board. Run one prompt as written, reset, then run the skill-enabled version. Compare diff size, verification evidence, and unnecessary complexity.

Open the starter project →
THE MISSING FEATUREAdd All / Open / Done filters that survive reload and browser navigation.
STACK HTML · CSS · JavaScript DEPENDENCIES none FILES 3
RUN AGood prompt
Clear context · constraints · acceptance · evidence
RUN BGood prompt + skills
Same contract · explicit working methods · stronger proof
COMPARE THE RUNS
01 Files changed
02 New dependencies
03 Checks actually run
04 Evidence returned

+
Hands-on10 minutes / one missing feature

Tiny Tasks lab

Same task.
Better operating system.

Start with a deliberately incomplete static task board. Run one prompt as written, reset, then run the skill-enabled version. Compare diff size, verification evidence, and unnecessary complexity.

THE MISSING FEATUREAdd All / Open / Done filters that survive reload and browser navigation.
STACK HTML · CSS · JavaScript DEPENDENCIES none FILES 3
RUN AGood prompt
Clear context · constraints · acceptance · evidence
RUN BGood prompt + skills
Same contract · explicit working methods · stronger proof
COMPARE THE RUNS
01 Files changed
02 New dependencies
03 Checks actually run
04 Evidence returned

-
Keep learning12 new readings + primary docs

Go deeper with official documentation, production case studies, Medium, and practitioner workflows. Primary references → 12-part reading path →

+
Keep learning12 new readings + primary docs

Go deeper with official documentation, production case studies, Medium, and practitioner workflows. Rules and enforcement case study → Primary references → 12-part reading path →

diff --git a/responsive.css b/responsive.css index a3ae772..856aa2c 100644 --- a/responsive.css +++ b/responsive.css @@ -65,7 +65,7 @@ button{font-family:inherit} .install-skills{display:grid;grid-template-rows:auto 1fr auto;margin-top:24px;color:var(--paper);background:var(--deep);border-left:7px solid var(--gold)}.install-skills header{display:flex;justify-content:space-between;align-items:center;gap:24px;padding:20px 24px;border-bottom:1px solid #ffffff2d}.install-skills header>div{display:grid;gap:7px}.install-skills header span,.install-skills footer{color:var(--gold);font:500 8px 'DM Mono',monospace;letter-spacing:.08em}.install-skills header strong{font-size:15px}.install-skills button{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid #ffffff50;color:var(--paper);background:transparent;cursor:pointer}.install-skills button:hover,.install-skills button.copied{color:var(--ink);border-color:var(--gold);background:var(--gold)}.install-skills button i{font-style:normal}.install-skills pre{max-height:360px;margin:0;padding:24px;overflow:auto;white-space:pre-wrap;background:#0b1b27}.install-skills pre code{font:10px/1.7 'DM Mono',monospace}.install-skills footer{padding:16px 24px;color:#b9c8d1;border-top:1px solid #ffffff2d}.install-skills button:focus-visible{outline:3px solid var(--gold);outline-offset:3px} /* Copy-ready hands-on lab */ -.hands-on{margin-bottom:130px;padding-top:80px;border-top:1px solid var(--line)}.hands-intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.hands-intro h2{margin-bottom:0}.hands-intro>div:last-child>p{max-width:650px;margin:0;color:var(--muted);font-size:14px;line-height:1.75}.starter-link{display:inline-block;margin-top:18px;color:var(--blue);font:700 11px 'DM Mono',monospace;text-decoration:none;border-bottom:2px solid var(--gold)} +.hands-on{margin-bottom:130px;padding-top:80px;border-top:1px solid var(--line)}.hands-intro{display:grid;grid-template-columns:.9fr 1.1fr;gap:70px;align-items:end;margin:45px 0}.hands-intro h2{margin-bottom:0}.hands-intro>div:last-child>p{max-width:650px;margin:0;color:var(--muted);font-size:14px;line-height:1.75}.starter-link{display:inline-block;margin-top:18px;color:var(--blue);font:700 11px 'DM Mono',monospace;text-decoration:none;border-bottom:2px solid var(--gold)}.starter-links{display:flex;flex-wrap:wrap;gap:6px 24px;margin-top:18px}.starter-links .starter-link{margin-top:0} .exercise-brief{display:grid;grid-template-columns:190px 1fr;gap:22px;padding:26px 30px;color:var(--paper);background:var(--deep)}.exercise-brief>span{color:var(--gold);font:500 9px 'DM Mono',monospace;letter-spacing:.09em}.exercise-brief>strong{font-size:clamp(20px,2.6vw,34px);line-height:1.12}.exercise-brief>div{grid-column:2;color:#afbec7;font:500 9px 'DM Mono',monospace;letter-spacing:.05em}.exercise-brief b{margin-left:18px;color:var(--accent)}.exercise-brief b:first-child{margin-left:0} .prompt-compare{display:grid;grid-template-columns:1fr 1fr;gap:1px;margin-top:1px;background:var(--line)}.prompt-card{display:grid;grid-template-rows:auto 1fr auto;min-width:0;min-height:600px;color:var(--paper);background:#19364a}.prompt-card.enhanced{background:#596f9a}.prompt-card header{display:flex;justify-content:space-between;align-items:center;padding:20px 22px;border-bottom:1px solid #ffffff32}.prompt-card header>div{display:grid;gap:6px}.prompt-card header span,.prompt-card footer{font:500 8px 'DM Mono',monospace;letter-spacing:.09em}.prompt-card header>div span{color:var(--gold)}.prompt-card header strong{font-size:17px}.prompt-card button{display:flex;align-items:center;gap:12px;padding:10px 12px;border:1px solid #ffffff50;color:var(--paper);background:transparent;cursor:pointer}.prompt-card button:hover,.prompt-card button.copied{color:var(--ink);border-color:var(--gold);background:var(--gold)}.prompt-card button i{font-style:normal}.prompt-card pre{margin:0;padding:25px;overflow:auto;white-space:pre-wrap}.prompt-card pre code{font:11px/1.72 'DM Mono',monospace}.prompt-card footer{padding:17px 22px;color:#bfccd4;border-top:1px solid #ffffff32}.prompt-card.enhanced footer{color:#e5e3ef}.prompt-card button:focus-visible,.starter-link:focus-visible{outline:3px solid var(--gold);outline-offset:3px} .comparison-strip{display:grid;grid-template-columns:190px repeat(4,1fr);gap:1px;background:var(--line)}.comparison-strip>*{padding:18px;background:var(--paper)}.comparison-strip>span{color:var(--accent);font:700 9px 'DM Mono',monospace;letter-spacing:.08em}.comparison-strip>div{color:var(--muted);font:500 10px 'DM Mono',monospace}.comparison-strip b{margin-right:8px;color:var(--blue)}.copy-status{min-height:20px;margin:15px 0 0;color:var(--blue);font:600 10px 'DM Mono',monospace;text-align:right}