Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Guards fire pre-hoc regardless of lifecycle stage. Override: `<GATE>: SKIPPED pe
| 7 | Milestone-ID Gate | `docs/gates/milestone-id.md` | Saving source / config outside `docs/`. Regex `M[0-9]+_[0-9]+`, `§X.Y`, `T7`, `dim N.M` · `MILESTONE ID ALLOWED per user override (reason: ...)` in preceding comment. |
| 8 | Architecture Consult & Update Gate | `docs/gates/architecture.md` | Naming a stream/channel/Redis namespace/queue/RPC/Postgres schema; describing a flow; mid-task architecture question — grep relevant `docs/architecture/` · **no override** — doc wins until reconciled. |
| 9 | ZIG GATE | `docs/gates/zig.md` | `*.zig` outside `vendor/`/`third_party/`/`.zig-cache/` (tests in scope) · `ZIG GATE: SKIPPED per user override (reason: ...)`. |
| 10 | Pub Surface & Struct-Shape Gate | `docs/gates/pub-surface.md` | New `*.zig` under `src/`; threshold-cross (first pub type / first method-on-pub-free-fn-dominant / last pub free fn removed); **any new `^pub` line in new-bytes** (threshold list is a floor, not a ceiling) · `PUB GATE: SKIPPED per user override (reason: ...)`. **`FILE SHAPE DECISION` skip needs user's explicit ask this turn — auto-mode does NOT cover.** **No inheritance:** each new pub surface requires its own consumer-grep + shape verdict; cloning a sibling's "Public for the integration test in …" comment does NOT discharge. **Per-edit proof-line mandatory** (full block OR one-line `PUB GATE: skipped — <reason>`); silent gate-clean edits are violations. |
| 10 | Pub Surface & Struct-Shape Gate | `docs/gates/pub-surface.md` | New `*.zig` under `src/`; threshold-cross (first pub type / first method-on-pub-free-fn-dominant / last pub free fn removed); **any new `^pub` line in new-bytes** (threshold list is a floor, not a ceiling) · `PUB GATE: SKIPPED per user override (reason: ...)`. **`FILE SHAPE DECISION` skip needs user's explicit ask this turn — auto-mode does NOT cover.** Consumer-grep delegated to `zlint`'s `unused-decls: error`; body enforces **shape verdict** + **no inheritance** (own verdict per surface; do not inherit sibling justifications). **Per-edit proof-line mandatory** (full block OR one-line `PUB GATE: skipped — <reason>`); silent gate-clean edits are violations. |
| 11 | UI Component Substitution Gate | `docs/gates/ui-substitution.md` | `*.tsx`/`*.jsx` under `ui/packages/app/`. Raw HTML (`<section>`/`<button>`/`<input>`/`<article>`/`<dialog>`/`<dl>`/`<table>`/`<nav>`/`<header>`/`<form>`) → design-system primitive (`asChild` for HTML semantics) · `UI GATE: SKIPPED per user override (reason: ...)`. |
| 12 | DESIGN TOKEN GATE | `docs/gates/design-token.md` | `*.tsx`/`*.jsx` under `ui/packages/app/` or `ui/packages/website/`. Blocks `*-[...]` arbitraries (`text-[Npx]`, `leading-[...]`, `tracking-[...]`, `max-w-[Npx|Nch]`, `text-[clamp(...)]`, raw palette) when token utility exists in `theme.css` · `// DESIGN TOKEN: SKIPPED per user override (reason: ...)` immediately preceding the line; auto-mode does NOT cover; reason must cite a concrete external constraint. |
| 13 | UFS GATE | `docs/gates/ufs.md` | Source under `src/`, `ui/packages/*/`, `zombiectl/` matching `*.zig`/`*.ts`/`*.tsx`/`*.js`/`*.jsx`. Repeat string literals → named const; semantic numeric literals → named const; cross-runtime constants share identifier verbatim across Zig/TS/JS. Pin-test exception requires `// pin test: literal is the contract` · `UFS GATE: SKIPPED per user override (reason: ...)`; auto-mode does NOT cover. |
Expand Down
4 changes: 2 additions & 2 deletions AGENTS_INVARIANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ The questionnaire is organised by scenario. Each scenario corresponds to a momen
| 4.1c (UI) | Does the project-side `scripts/audit-design-tokens.sh --diff` audit run as part of `make lint` (`_website_lint` + `_app_lint`) and block on any arbitrary that has a token equivalent? | YES |
| 4.2 (Zig) | For every `*.zig` Edit/Write outside `vendor/`/`third_party/`/`.zig-cache/`, does ZIG GATE fire? | YES |
| 4.3 (Zig) | Must FILE SHAPE DECISION print before the first Write to a new `*.zig` under `src/` — and is that override **not** covered by auto-mode? | YES |
| 4.4 (Zig) | Does PUB GATE require external-consumer grep proof for every new `pub` symbol? | YES |
| 4.4a (Zig) | Does AGENTS.md prohibit inheriting a sibling's `pub fn` decision — i.e. does every new pub surface require its own consumer-grep + shape verdict, and does cloning a sibling's "Public for the integration test in …" comment NOT discharge the gate? | YES |
| 4.4 (Zig) | Does PUB GATE delegate mechanical consumer-grep to `zlint`'s `unused-decls: error` rule (run by `make lint`), leaving the gate body to enforce shape verdict + no-inheritance + per-edit proof? | YES |
| 4.4a (Zig) | Does AGENTS.md prohibit inheriting a sibling's `pub fn` decision — i.e. does every new pub surface require its own shape verdict, and does cloning a sibling's "Public for the integration test in …" comment NOT discharge the gate? | YES |
| 4.4b (Zig) | Does AGENTS.md require a per-edit proof-line (full block OR one-line `PUB GATE: skipped — <reason>`) before every `*.zig` Edit/Write, so silent gate-clean edits are still violations (the proof of consideration is part of the discipline)? | YES |
| 4.4c (Zig) | Does the PUB GATE trigger set explicitly include "any new `^pub` line in new-bytes" (the pre-edit grep at body §1), so the threshold list (first pub type / first method on pub-free-fn-dominant / last pub free fn removed) is a floor, not a ceiling? | YES |
| 4.5 (Zig) | Is cross-compile to `x86_64-linux` AND `aarch64-linux` mandatory before commit? | YES |
Expand Down
1 change: 1 addition & 0 deletions docs/ZIG_RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ For every commit that touches `*.zig`, the agent runs the workflow below — no

- This repo uses `zlint` as part of `make lint`.
- Pinned version: `v0.7.9`.
- **`unused-decls: error` is load-bearing.** PUB GATE (`docs/gates/pub-surface.md`) delegates mechanical consumer-grep to this rule — a `pub` without an in-tree consumer fails `make lint`. Disabling or downgrading it silently bypasses half the gate; if you must, amend PUB GATE in the same diff so the design call is captured elsewhere.
- `suppressed-errors` stays off because this repo intentionally uses narrow `pg` cleanup patterns that a generic rule cannot classify correctly.
- `unsafe-undefined` is a good future tightening target once current low-level uses are cleaned up or annotated.
- A disabled ZLint is not useful; prefer a scoped ruleset that passes today and tightens over time.
Expand Down
18 changes: 7 additions & 11 deletions docs/gates/pub-surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ For all other `*.zig` edits, the full block is skipped with a one-liner: `PUB GA

Two `docs/ZIG_RULES.md` rules:

1. **`pub` only when an external file imports the symbol** — default private; strip stale `pub`s when touching a file.
1. **`pub` only when an external file imports the symbol** — default private; strip stale `pub`s when touching a file. *Mechanical enforcement is delegated to `zlint`'s `unused-decls: error` rule*, which fails `make lint` for any `pub` without an in-tree consumer. The gate body no longer requires a per-symbol consumer-grep in the proof block.
2. **File-as-struct shape is the default for new behavior-bound-to-state files.** A file *is* a struct in Zig — modeling that shape exposes ownership and testability cheaply. Conventional layout (multi-type modules, parsers/DSLs, constants modules, pub-free-fn-dominant modules, tagged-union dispatch tables, "operations over a passive value") **must be justified at PLAN**, not chosen by inertia.

**Tie-break:** behavior-bound-to-state → file-as-struct; operations-over-value → conventional. **Escape clause:** "I can articulate in one sentence why this is operations-over-value." If you can't, file-as-struct wins.

**No inheritance:** each new pub surface needs its own *shape verdict* — cloning a sibling's "Public for the integration test in …" justification does NOT discharge. Mechanical consumer-grep is `zlint`'s job; the design call is yours, and it does not transfer between symbols.

## File-as-struct layout

```zig
Expand Down Expand Up @@ -70,21 +72,15 @@ Skipping this block is a PLAN violation, not just an EXECUTE one.
- new variant on a pub error union: `ErrorUnion{… NewVariant,`
- new variant on a pub enum: `= enum { … NewVariant,`
2. Count primary types in the file (struct/union/enum the file is "about"); choose layout: file-as-struct (count = 1, all pub fns take `self`) or conventional (otherwise).
3. List every new `pub` symbol the edit introduces (top-level + variant additions); for each, grep external consumer:

```
grep -rn "<symbol>" src/ tests/ --include="*.zig"
```
3. List every new `pub` symbol the edit introduces and confirm its shape verdict matches the file's chosen layout. Each new symbol gets its own verdict — do not inherit from a sibling.

→ file:line, or `NONE`. Strip `pub` from any with `NONE`.
4. Progressive cleanup on touch: `grep -n "^pub " <file>` and audit existing `pub`s in the same diff.
Mechanical consumer-grep is no longer part of this checklist: `zlint`'s `unused-decls: error` rule (enabled in `zlint.json`, run by `make lint`) fails the build on any `pub` without an in-tree consumer. The gate body owns the design call (shape verdict + no inheritance); zlint owns "did you forget to strip a dead pub?"

## Required output (when fires)

```
PUB GATE: <file> | types=<0|1|>1> | layout=<file-as-struct|conventional> (<why>)
New: <sym> consumer=<file:line>|NONE→strip; <sym> consumer=...
Audited: <K> kept · <M> stripped
New: <sym>, <sym>, …
```

If no new `pub` symbols and the file is not new, the gate is a no-op — skip the printable block.
Expand All @@ -97,7 +93,7 @@ git diff -U0 HEAD -- '*.zig' \
| head
```

Non-empty = new pub surface this turn; verify a PUB GATE block was printed before each corresponding Edit/Write.
Non-empty = new pub surface this turn; verify a PUB GATE block (with shape verdict) was printed before each corresponding Edit/Write. The end-of-turn `make lint` run is also load-bearing: if `zlint` reports `unused-decls`, you skipped stripping a dead `pub` and the gate did not fail loud enough.

For new-file FILE SHAPE coverage:

Expand Down
Loading