9015e7bd1d
Removes the Go source, Dockerfile, go.mod, and Kubernetes manifests. The
deployed service is untouched and the review desk still calls it over
window.SKILLS_REVIEW_VOTE_API; only the source leaves.
The runbook does not leave. vote-service/README.md moves to
docs/vote-service.md, because it carries the parts that are hard to
rediscover: why the ingress overwrites X-Forwarded-For and Caddy stamps
X-Client-IP instead, why the image is side-loaded into containerd rather
than pulled, and why the PVC pins the Deployment to one node.
This drops verify.mjs from 84 assertions to 83. The removed one read
vote-service/main.go for X-Forwarded-For and 'one active vote per skill'
-- the review desk's only anti-abuse control -- and there is no file left
to read. It is the first assertion this repository has ever lost.
Rather than lower the gate's floor and leave a bare number behind,
gate.sh now subtracts the number of entries in
.agents/context/assertion-removals.md from the baseline. A removal costs
a written reason in a tracked file, in the same commit, as a visible
diff. Tested at 82 assertions: still refused.
Also drops the 22 MB of PNG baselines under .agents/snapshots/before/ and
before-reduced-motion/. They pictured the hand-written site, which no
longer exists; visual-regression.mjs has no compare mode to diff them
against; and they are recoverable from d88d8b8.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2.8 KiB
2.8 KiB
Rule: Astro
Binding for every .astro file.
Zero JS is the default
A component ships no JavaScript unless it has a client:* directive. Seven of
this site's ten pages ship no JS today and must continue to.
- Never add
client:loadwithout justifying it in the PR description. - Prefer, in order: no JS →
client:visible→client:idle→client:load. - An island is a leaf, not a wrapper. Hydrate the tab panel, not the page.
Islands in this project
Only these need interactivity. Anything else claiming island status is wrong:
| Island | Why | Directive |
|---|---|---|
| Guide phase/tab switchers | click-driven panel swap | client:visible |
| Review desk catalog + file viewer | search, filter, fetch source files | client:load |
| Vote widget | talks to the vote API | client:visible |
| Language toggle | swaps EN/PT across the page | client:idle |
Structure
---
// 1. imports
// 2. Props interface
// 3. destructure Astro.props
// 4. derived values — no side effects, no fetch in components
---
<!-- markup -->
<style>
/* component-scoped */
</style>
- Typed props always:
interface Props { … }, thenconst { … } = Astro.props. - Data loading belongs in
src/content/collections or the page frontmatter, not inside a component. - No barrel files (
index.tsre-export hubs). They cost tree-shaking and invite cycles.
Content collections
All copy lives in src/content/, typed with a Zod schema in
src/content/config.ts. The review desk's catalog.js maps onto a collection
almost one-to-one — do that rather than importing a 27 KB JS file.
Styles
- Component styles go in the component's
<style>block. Astro scopes them. - Only tokens and true resets live in global CSS.
- Do not port
responsive.cssverbatim. It is an override layer whose reason for existing disappears once layout is componentized. Port what a component needs, prove the rest is dead, delete it.
URLs and the base path
The site is served from /ai-for-dummies/. Set base in astro.config.mjs and
never hand-write an absolute internal path. Use import.meta.env.BASE_URL.
Existing routes are load-bearing and must not change, including trailing slashes and the review desk's query params.
Never
- No UI framework (React/Vue/Svelte) unless a task brief explicitly calls for it. Astro components plus a little vanilla JS cover everything here.
- No CSS framework. This site has a hand-built visual identity — see
theming.md. - No external runtime requests. Self-host.
audit-ui.mjsenforces it.