Build

Rules

Apps Readme

3

Naming follows a **(domain, leaf) derivation** model.

Apps Readme

When editing an `apps/*/README.md`, MUST have YAML frontmatter with these required fields: `name` (human-readable display name), `description` (1–3 sentences; use `>-` for multiline preservation), `schedule` (freeform timing string e.g.

Apps Readme

When authoring an `apps/*/README.md` from scratch, MUST follow this canonical frontmatter scaffold (absorbed from the deleted `.claude/templates/app-readme.md`): ```yaml --- name: App Display Name description: >- 1-3 sentence description of what the app does.

Apps Readme

Apps Scripts

1

When generating AI text in an apps/ script, MUST use the cross-provider router; MUST NOT call LLM APIs directly.

Apps Scripts

Behavior Platform Contracts

2

Before tuning a config to a rate limit, quota, or API constraint from a third-party doc, MUST run one empirical probe to confirm the actual observed limit; MUST NOT trust documented limits as observed behavior.

Behavior Platform Contracts

When applying a fix that depends on a managed platform honoring a config setting (Windmill CE `concurrent_limit`, Cloudflare KV limits, etc.), MUST empirically verify before shipping (fire the limit, observe enforcement); MUST NOT trust an unverified platform contract.

Behavior Platform Contracts

Behavior Tests

1

Before declaring a new test complete, MUST verify it fails on the known-regressed state (revert the fix, run the test, confirm failure, re-apply the fix); MUST NOT rely on "the test passes" as sufficient evidence.

Behavior Tests

Behavior

9

When implementing any new computation or flow, MUST enumerate expected behavior at 0, 1, empty, max, and error inputs.

Behavior

When the diagnosed design flaw is a **class-level mechanism** (affects more than one file/script: shared pattern across siblings, orchestrator families, repeated anti-pattern), MUST land every instance the Step 1.7 peer-sweep finds in the same session.

Behavior

A session that produced a new app or a major change to an existing app (new file under `apps/**/` at any nesting depth — including domain-grouped `apps/{domain}/{name}/` — or >50 lines added/modified across `apps/**/*.ts` ∪ `integrations/**/*.ts`) MUST be machine-reviewed by `/implement-audit` on **Codex, a different vendor from the author**, BEFORE the change lands.

Behavior

When the task is a DECISION (architecture, strategy, vendor, process, >$500 impact), MUST present alternatives + trade-offs + flagged risks and wait for user selection; MUST NOT execute unilaterally.

Behavior

When a task has an authoritative deterministic source (REST API, web scrape with a stable HTML anchor, computation, lookup table, repo file), MUST use that source instead of an AI call; MUST NOT substitute AI inference for data that can be fetched, computed, or parsed.

Behavior

Pre-send completion test: before ending ANY turn, every task surfaced this turn MUST be either (a) DONE now, or (b) explicitly blocked on the user with the blocker named.

Behavior

MUST NOT build hooks, checks, or rules for failure modes that have not yet occurred.

Behavior

When planning any non-trivial work, MUST address all four before starting implementation: downstream consumers (grep for users), peer consistency (find sibling files doing similar), edge cases (enumerate), doc surface (which docs drift).

Behavior

Every work product produced in a turn MUST terminate in one of two destinations: (a) printed directly in the assistant response to the user, or (b) a file inside the context repo.

Behavior

Compose Files

1

After editing an `integrations/*/compose*.yml` file, MUST run `integrations/komodo/deploy-stack.sh <service>` in the same session; MUST NOT expect the compose change to apply automatically — Komodo deploys the specified service to the configured target.

Compose Files

Data Reliability

18

At request/SSR time (Astro `.astro` frontmatter, API routes, middleware, any per-request handler), MUST NOT `await` a KV / DB / `fetch` read inside a loop over a key list or a collection — that is O(N) sequential round-trips and is the recurring slow-page bug.

Data Reliability

Business logic MUST live in TypeScript independent of orchestrator (Hatchet handles scheduling/concurrency/retries; the orchestrator is infrastructure); MUST NOT couple business logic to orchestrator APIs beyond the platform-provided primitives (`task.run`, `step` outputs, `concurrency` config).

Data Reliability

When a piece of metadata describes a data product (SLA, data sources, frontmatter), MUST co-locate it with the code that produces the product (script header for writer SLA; `pageInfo` for reader data sources; README frontmatter for app); MUST NOT create standalone inventory files.

Data Reliability
derive-inventories-from-code

[Folded into @rule:co-locate-metadata on 2026-05-13 — `derive-inventories-from-code` lived here from 2026-03-28 to 2026-05-13] [2026-05-13]

Data Reliability

`validateOutcome` MUST check correctness (expected fields non-null, arrays non-empty when expected), not just existence; warnings MUST be treated as failures; MUST NOT display zeros or blanks from missing data as real values.

Data Reliability

Every automation MUST be idempotent — running twice yields the same result; MUST NOT rely on append-only or increment-only side effects; state is derived from inputs, not accumulated.

Data Reliability
new-client-data-goes-to-extension

[Folded into @rule:no-mrr-clients-duplication on 2026-05-13 — `new-client-data-goes-to-extension` lived here from 2026-03-28 to 2026-05-13] [2026-05-13]

Data Reliability

When adding client-level data used by all three WSP portals, MUST add to `mrr:clients` (FINANCE_DATA); when data is portal-specific, MUST add to that portal's extension namespace joined by codename; MUST NOT duplicate `mrr:clients` fields (codename, clientName, type, amount, dates, users, atRisk flags) into portal-specific KV.

Data Reliability

For any external data source, MUST consolidate to one fetch per schedule-group with many writes from it; MUST NOT issue multiple independent fetches of the same entity at different times — contradictory snapshots corrupt downstream reads.

Data Reliability

Every KV key MUST have exactly one script that owns writes and declares an SLA; MUST NOT introduce a KV key whose owner or SLA is ambiguous.

Data Reliability

When a fetch returns garbage (schema mismatch, missing required fields, Zod validation fails), MUST refuse to write and keep the last good value with a staleness warning; MUST NOT overwrite good data with bad.

Data Reliability
stale-data-expires-hard-ttlmechanism

Every **timer-SLA** data product MUST declare a hard TTL (typically 2-3× the SLA); after TTL, DataHealth MUST show "missing" not "stale"; MUST NOT display last month's financials as merely "stale." [2026-03-28]

Data Reliability

CF Pages build command MUST run `astro check` (or `tsc --noEmit`) then `astro build`; MUST NOT deploy on type errors; MUST NOT skip the check.

Data Reliability

Every external API access MUST go through a typed client co-located with the fetch script; MUST NOT use raw `fetch()` for external APIs against known integration hosts (`hosts:` frontmatter from `integrations/*/README.md`).

Data Reliability

Production code MUST be TypeScript; MAY use shell ONLY for thin wrappers around other tools (deploy scripts, hook glue); MUST NOT implement business logic in shell.

Data Reliability

For every external fetch, MUST assert at least one domain invariant before using the data (date range overlaps the request, result count ≥ expected floor, entity types match the query); MUST NOT trust a valid schema as sufficient — valid-but-wrong data is silent corruption.

Data Reliability
writer-header-declares-products

[Folded into @rule:one-owner-one-sla-per-key on 2026-05-13 — `writer-header-declares-products` lived here from 2026-03-28 to 2026-05-13] [2026-05-13]

Data Reliability

Every read and write boundary for external data MUST validate via Zod; MUST NOT accept or emit external data with unvalidated shape; schema drift MUST break the build, not the runtime.

Data Reliability

Feedback

21

When a task surfaces items only Ashkaan can resolve (unclassifiable data, a taxonomy boundary, a naming call), MUST batch them into one `AskUserQuestion` at the moment they surface; MUST NOT hold them until the end, park them, or wait to be asked "any questions?".

Feedback

When solving a problem requires modifying shared infrastructure on any host other than the repo working tree — host networking (`ip link/addr/route`, NetworkManager, systemd-networkd), daemon configs (`/etc/docker/daemon.json`, `/etc/ssh/sshd_config`, `/etc/systemd/...`, `/etc/cni/...`), creating or enabling systemd unit files outside `~/.config/systemd/user/`, modifying root-owned credentials (`/root/.docker/config.json`, `/root/.ssh/`, `/etc/op/...`), or anything that persists across reboots on a shared host (Hermes, the WSP portal hosts, MNK servers) — MUST stop and `AskUserQuestion` BEFORE executing, even if the change is "small," "additive," "documented workaround," "reversible," or "the standard fix." Diagnose freely (read-only).

Feedback

Universal, every reply (not just answers): use fewer words and less jargon.

Feedback

Each create/edit/write/execute under `.claude/` is a separate user approval prompt (harness-gated, not governed by `settings.json`, not disableable).

Feedback

When implementing, deploy is part of the work — MUST NOT ask "want me to deploy?", offer it as a follow-up, or list "Deploy X" as a pending item.

Feedback

Before any design artifact (SPEC, plan, code, Codex consensus, agent dispatch), MUST present two plain-English sections — **Goal:** (one sentence to a few bullets, sized to the work) and **Simplest mechanism:** (the lightest-weight way to achieve it) — then MUST wait for explicit approval.

Feedback

When the user asks "which would you pick?", "what's best?", "what would you choose if greenfield?", or any other question seeking a single recommendation, MUST reason from greenfield: ignore sunk cost, migration cost, and Claude's own work cost — the user does not bear those.

Feedback

Before asserting Claude lacks access to any external service, system, or data source ("I can't access X", "the agent couldn't access X"), MUST check three sources in order: (a) `integrations/<service>/README.md` exists — follow its access pattern; (b) `op item list --vault "AI Context" | grep -i <service>` matches — creds are stored; (c) the service appears in a Komodo stack list if containerized — access flows through `integrations/komodo/`.

Feedback

Ashkaan lives in **West Hills, CA** (~91307), timezone America/Los_Angeles — SSOT `knowledge/people/ashkaan-hassan/README.md § Identity`.

Feedback

When proposing to "lock in" / "select" / "finalize" / "choose" a runtime, framework, vendor, major library, or major version for repeated use across the repo, MUST first run BOTH (a) a workload-shaped smoke test that exercises the smallest end-to-end production behavior the technology will actually perform — NOT just module load + API-surface init + a connection; AND (b) an external-evidence search covering the vendor's public issue tracker, the official compatibility/test matrix, and an authoritative current-LTS/current-stable lookup against the live registry — NOT training-data recall.

Feedback

When the user has explicitly authorized an action this turn (verbally, via `AskUserQuestion`, or a "do it" / "yes" / "I approve" directive), MUST execute it end-to-end myself.

Feedback

When invoking the `Agent` tool, MUST NOT pass any non-default `isolation:` value — no isolation is the only acceptable shape; the subagent shares the main session's filesystem, and a change-bearing isolated worktree orphans a parallel checkout on a synthetic branch the orchestrator must then consume.

Feedback

Before asserting any fact about current repo state, platform contracts, API limits, versions, counts, timezones, or dates, MUST investigate with a tool (Read, Grep, Glob, WebFetch, curl, gh) in THIS session; MUST NOT assert from training data or plausible inference — training-data recall for factual questions is a violation.

Feedback

MUST NOT add fmt, lint, or similar auto-quality enforcement that covers `.md` files in hooks, scripts, or CI; markdown is not code.

Feedback

When executing a `systemctl restart|stop|disable` (or any process-killing action) against a service whose process tree contains the current Claude session's runtime, MUST stop and ask the user — killing it terminates the session issuing the command.

Feedback

Before invoking a skill (`/project`, `/solve`, `/implement-audit`, etc.), MUST verify the skill is either (a) explicitly requested by the user in the CURRENT message, OR (b) invoked as a declared sub-skill by another skill's step graph already active in the session; MUST NOT trigger skills on keyword matches in user answers or incidental conversation flow — user answers to `AskUserQuestion` are responses, not skill invocations.

Feedback

When a SPEC review, an `/implement` verify-assumptions discovery, or any other mid-implementation surface introduces ANY new infrastructure dependency or scope addition that was NOT in the Step-0 goal+shape the user said yes to — host-infra changes, new 1P items, new env-var categories, new shared-helper signatures beyond pure additive backwards-compat, `/etc/hosts` edits, supervisor/compose changes, "fall back if X fails" branches on systems the user hasn't seen, or re-using an existing cred for a new purpose — MUST stop and surface it to the user as a plan-revision conversation BEFORE folding it into the SPEC AND BEFORE executing it.

Feedback

When verifying or QA'ing a change to a CF-Access-gated portal (finance / operations / sales / dashboard), MUST verify against production KV (`cfKvRead` / REST) or screenshot the live gated page with the dashboard-probe service token, per `integrations/cloudflare/README.md § "Verifying a portal feature"`; MUST NOT conclude a change is "unverifiable locally" or "needs deploy first" because the page is auth-gated — a CF-Access gate is a login wall for my browser, never a data wall.

Feedback

When building or migrating an automation that observes or acts on an external system, MUST research what each target exposes (cross-network API / push-webhook / SSH-only) BEFORE picking a transport, and MUST prefer the cross-network API (HTTP/REST/S3) over SSH whenever one exists AND it does not lengthen the runner→target path; MUST NOT default to SSH without checking.

Feedback

When QA'ing visual UI output (rendered slides, screenshots, page renders, design candidates, portal pages) before shipping a change to a deployed surface, MUST dispatch a fresh-context subagent for the visual review; MUST NOT inline-QA by reading PNGs in the main orchestrator context.

Feedback

Default to the simplest mechanism that works.

Feedback

Google Urls

1

For Google URLs (Sheets, Docs, Drive, Calendar, Gmail, Contacts), MUST use the typed clients in `integrations/google/` (`sheetsApi`, `gmailApi`, `calendarApi`, `contactsApi`); MUST NOT use WebFetch on those URLs — they require auth and always fail with 401/403.

Google Urls

Governance Apps

5

When an app's SPEC declares `ai_judgment_features:` (non-empty list in YAML frontmatter), MUST include `apps/{name}/evals/eval.ts` + at least one fixture file (`apps/{name}/evals/fixtures/*.json`); MUST NOT ship a new AI-judgment app or feature without an eval.

Governance Apps

When adding a major feature to an existing app under `apps/{name}/`, MUST author `apps/{name}/specs/YYYY-MM-DD_feature-slug.md` with the 9-section schema (mirror an existing app's SPEC.md) before any implementation code is written; SPEC persists as long-term documentation.

Governance Apps

When creating a new app under `apps/{name}/` whose implementation includes `.ts`/`.tsx` files (automation, Windmill script, edge worker, etc.), MUST include `apps/{name}/SPEC.md` with the 12-section schema (mirror `integrations/trigger-dev/new-app-guide.md § SPEC.md` for the canonical template), covering all 11 base sections: (0) user's ask verbatim, (0a) how we interpreted this, (0b) simplest shape that could work, (1) behavior contract, (2) input contract (trigger, schedule, params, credentials), (3) output contract (KV writes with envelope type + SLA, stdout shape, side effects), (4) boundary cases at 0 / 1 / empty / max / error, (5) one acceptance command with inline expected stdout, (6) test plan pointer, (7) data-reliability checklist, (8) failure modes — plus section 9 (AI Eval Plan) when the SPEC declares `ai_judgment_features:` non-empty in YAML frontmatter (per `@rule:apps-eval-required-for-ai`).

Governance Apps

When creating a new app under `apps/{name}/` that adds `.ts`/`.tsx` code (beyond test files themselves), MUST include at least one paired test file exercising the acceptance behavior (`apps/{name}/*.test.ts` or `apps/{name}/tests/*.ts`).

Governance Apps

MUST NOT send email directly from arbitrary code.

Governance Apps

Governance Integrations

2

Writes to the context repo from automation code (apps/, integrations/) MUST go through `integrations/github/git_local.ts` (`commitViaSsh` — SSH to the repo host, commit in a per-automation worktree) — NOT through the GitHub REST API.

Governance Integrations

When writing or modifying a typed API client for an external integration, MUST place the canonical reference at `integrations/{name}/{name}.ts`; Daedalus-side code (apps, `projects/**/*.ts`) MUST import the reference directly via filesystem relative path.

Governance Integrations

Governance Projects

4

The shard model is **opt-in**, for projects deliberately split into ≥2 independent parts that can be pursued in parallel.

Governance Projects

When a project's status is `blocked`, MUST use `blocked-on:` frontmatter field naming what's blocking; MUST NOT use `next:` for blocked projects.

Governance Projects

MUST NOT autonomously set `status: completed` — only the user marks completion.

Governance Projects

When creating a new project, MUST place at `projects/{domain}/YYYY-MM-DD_name/` with `README.md` containing required frontmatter (project, status, created, tags); MUST push immediately after creation so the index regenerates.

Governance Projects

Governance Readme

1

When adding or editing a markdown table in a README whose first column lists files or subdirectories of the README's own directory, MUST either (a) auto-generate it via Phase A with a named reader or (b) delete it and replace with narrative; MUST NOT hand-maintain a file-listing table — drift is inevitable.

Governance Readme

Governance

6

When my own tools (curl, fetch, headless screenshot) cannot access a user-protected resource (CF Access, SSO, VPN, browser auth, IP allowlist), MUST NOT assume the user also cannot access it; MUST NOT escalate to a more-public mechanism (remove the auth gate, publish to a no-auth URL, upload to a public image host, route via a separate unauthenticated CDN) to solve my OWN tool-access problem.

Governance

Every git commit subject MUST begin with an actionable verb (add / fix / update / refactor / remove / etc.), ≤100 chars, no prefix-only-no-content shapes; MUST NOT use vague subjects ("wip without scope", "fix it", multi-line streams).

Governance

Before writing "shipped", "deployed", "live", "auto-deploys", or any deploy-command instruction in session output (close summary, implement-audit report, plan file, commit message), MUST consult the relevant `integrations/<name>/README.md` for the repo's actual deploy model and verify the claim against it; MUST NOT treat repo-local convenience scripts (`package.json` `deploy`, `Makefile` `deploy`, etc.) as authoritative when the integration README documents a different model.

Governance

Git commit messages MUST NOT include `Co-Authored-By: Claude ...

Governance

When a lint or format error occurs, MUST fix it at the source; MUST NOT add the file to an exclude list to avoid the fix.

Governance

When the user says "close", "wrap up", "let's close", MUST invoke `/close` (journal entry + commit + push).

Governance

Health Data

1

Health data MUST be stored in year-based files: `knowledge/health/{topic}/{year}.md`; MUST load current year by default; MUST NOT always-load historical years — pull only on-demand for trend analysis.

Health Data

Integrations

2

A folder under `apps/` holds repo-authored code — anything we wrote.

Integrations

A folder under `integrations/` MUST wrap an external product — third-party SaaS, OSS running on another host, vendor API, or a host-bound product we connect to.

Integrations

Journal

4

Every session block MUST start with `**Action:** <verb>` where `<verb>` is one of the closed set: `completed | progressed | scaffolded | fixed | investigated | planned | shipped | migrated | removed | updated`; MUST NOT omit, replace, or use a verb outside the enum.

Journal

When editing a journal entry, frontmatter MUST contain `date` (YYYY-MM-DD) and `tags` (list); MUST contain a `sessions:` block listing one `- slug: <heading>` entry per session worked that day (parsed by `.claude/hooks/telemetry/local_aggregator.ts`); MAY contain additional structured telemetry fields per @rule:journal-session-frontmatter; MUST NOT add unrelated ad-hoc frontmatter fields.

Journal

Journal entries MUST be structured (headings, bullets, labeled sections like Changes/Lessons/Blocked/Next); MUST NOT contain narrative prose, code diffs, or deploy play-by-play.

Journal

Each session in a journal MUST use a `###` heading matching one of two shapes: `one-off (<brief description>)` OR `<domain>/<YYYY-MM-DD>_<slug>` (optionally followed by ` (<parenthetical>)`, ` — <em-dash-tail>`, `/<sub-slug>`, or `_<sub-slug>`); MUST NOT use `##` or freeform section names.

Journal

Meta · Adversarial Tools

7

Adversarial tools MUST treat "zero findings" as a permitted output and report it explicitly; MUST NOT pad output with trivia or speculative items to avoid the empty-result case; linter flags outputs containing only `speculative` verdicts as probable padding.

Meta · Adversarial Tools

When `/implement-audit` exceeds 2 rounds on the same ship even after user override, MUST ship with remaining findings captured as deferred items in the project README; MUST NOT continue fix-round loops past round 3 — the implement-audit loop is self-generating because each fix creates new audit-able surface, and diminishing returns become negative returns.

Meta · Adversarial Tools

Adversarial tools MUST enforce a recursion cap of 2 rounds on the same ship; round 3 MUST invoke `AskUserQuestion` asking `ship | redesign | defer`, and log the choice; MUST NOT proceed to round 3+ without the user decision gate.

Meta · Adversarial Tools

Adversarial tools MUST operate only on changes in the current conversation or the named diff range; MUST NOT comment on unrelated repo state or other sessions' work.

Meta · Adversarial Tools

Adversarial-tool output MUST include a structured section (YAML fence with `findings: [{id, verdict, rule, message}]`) consumable by the telemetry aggregator, in addition to any human-facing prose; MUST NOT emit prose-only output.

Meta · Adversarial Tools

Adversarial-tool output MUST emit every finding with a triage verdict: `fix-now | deferred-batch-N | speculative | out-of-scope`; MUST NOT emit free-form "here's a list of findings" output — untriaged findings produce the fix-every-finding audit loop.

Meta · Adversarial Tools

Any adversarial or review tool (`/implement-audit`, `/review`, `/security-review`) MUST emit triaged findings (`fix-now | deferred-batch-N | speculative | out-of-scope`) AND enforce a recursion cap of 2 rounds on the same ship; on round 3 MUST `AskUserQuestion` for `ship | redesign | defer`; MUST NOT loop indefinitely.

Meta · Adversarial Tools

Meta · Ai Layer Authoring

8

When an agent runs work Ashkaan is not present to supervise, each of its hard limits MUST be enforced by withholding the tool that would breach it — scope the agent's `tools:` list — and MUST NOT rest on an instruction in its brief telling it not to.

Meta · Ai Layer Authoring

When authoring or dispatching a `.claude/` artifact that does LLM work whose work-type MATCHES a row in `apps/ai/prompt-router/policy.ts` (machine-readable mirror `policy.json`), the model MUST be the row's assignment; MUST NOT be chosen at the call site.

Meta · Ai Layer Authoring

When a rule is violated 3+ times despite being in the right file, MUST promote it to enforcement (hook, linter); MUST NOT leave a 3x-violated rule as prose-only.

Meta · Ai Layer Authoring

For "When X then Y" automation placement, MUST pick surface by what X is: a `git commit` (any committer — Claude, scripts, or trigger.dev SSH) → a **native git hook** under `.githooks/` per `@rule:tiebreaker-commit-hook-placement` (thin entrypoint → body in `.claude/hooks/bodies/`); any other Claude tool call → `.claude/hooks/` PreToolUse/PostToolUse with body scripts in `.claude/hooks/bodies/`; clock tick (declarative verification, scheduled checks, anything else) → trigger.dev (a scheduled `schedules.task`).

Meta · Ai Layer Authoring

For "Always think Y when reasoning about Z" guidance placement, MUST pick by scope of Z, DEFAULTING to the lightest deterministic trigger and treating always-loaded as the justified exception (per `@rule:rule-default-deterministic-trigger` + `@rule:no-model-judgment-jit`): mechanizable convention → a hook/linter; convention that only matters editing certain files → `.claude/rules/{topic}.md` with `paths:` frontmatter (path-scoped); task-typed guidance reliably signaled by a word that IS its activation condition → keyword-injected via `.claude/hooks/integration-match.sh`; judgment-core Claude behavior that applies every session AND cannot be checked or path-scoped → `.claude/rules/{topic}.md` with no `paths:` (always-loaded) AND a `<!-- judgment-core: <reason> -->` marker per `check-rule-minimal-context.sh`; persistent fact about a person → `knowledge/people/{name}/`; domain-specific → that domain's README; multi-step named operation → `.claude/skills/{name}/SKILL.md`.

Meta · Ai Layer Authoring

Every automation MUST emit at least one signal channel (post-commit echo, hook stderr, Daedalus notification); MUST NOT ship invisible automation — silent automations cannot be debugged or trusted.

Meta · Ai Layer Authoring

Commit-time automation MUST be authored as a **native git hook** under `.githooks/` (`core.hooksPath=.githooks`) — `pre-commit`, `commit-msg`, or `post-commit` — as a thin entrypoint delegating to the body script under `.claude/hooks/bodies/git-{precommit,commit-msg,postcommit}.sh`.

Meta · Ai Layer Authoring

When splitting work between skill and agent, MUST place in a skill if the work needs session history (orchestration, editing based on session state, user-facing slash commands, commit/push flows); MUST place in an agent if the work benefits from fresh context (adversarial review, cold-reader analysis, isolated investigation, parallel offload to protect the main context window).

Meta · Ai Layer Authoring

Meta · Foundations

5

Every behavioral rule MUST either run automatically (hook, linter) OR fire as a concrete tool call at a named decision point inside a skill step graph OR carry a `<!-- judgment-core: <reason> -->` marker per `@rule:rule-default-deterministic-trigger` justifying why neither is possible; MUST NOT be cited as enforcement if it exists only as advisory prose.

Meta · Foundations

When multiple surfaces constrain the same actor for the same action, MUST consolidate to the most powerful surface (hook > app > rule for enforcement); the mechanism's header comments are the declaration.

Meta · Foundations

The weekly telemetry aggregator (`local_aggregator.ts`) MUST flag rules that have not fired in the last 28 days AND have no `enforces:` references from skills/hooks; flagged rules are deletion candidates; MUST NOT let the rule-count grow monotonically without a deletion gradient.

Meta · Foundations

Every fact (rule content, step definition, peer declaration, wire format, schema) MUST live in exactly one file; MUST NOT be mirrored as prose across multiple files.

Meta · Foundations

When rules conflict or interpretation is ambiguous, MUST defer to Ashkaan; MUST flag conflicts for resolution; MUST NOT rewrite rules unilaterally.

Meta · Foundations

Meta · Hooks

5

Hooks MUST fall into one of nine allowed categories: (1) syntactic checks (lint/fmt/shellcheck/gitleaks/check-refs), (2) peer-file co-commit validation, (3) progress-doc co-commit validation, (4) checklist completion gating, (5) journal frontmatter validation, (6) memory-write redirect, (7) tool-sandbox blocks (block-context-builds, block-send-gmail), (8) session-discipline gate (refuse a tool/skill invocation that violates a session-shape invariant — e.g.

Meta · Hooks

Every blocking hook MUST emit an error message naming the exact file, the exact line (when applicable), and a concrete remediation; MUST NOT say "fix the issue" without pointing at the issue.

Meta · Hooks

Hooks MUST NOT subject `.md` files to syntactic checks (fmt, lint, linewidth); markdown is not code.

Meta · Hooks

Every hook shell script MUST start with `set -euo pipefail` (after shebang); MUST NOT let errors silently propagate — hooks with broken state fail hard, not quietly.

Meta · Hooks

Every hook shell script MUST pass `shellcheck` on commit; MUST NOT disable warnings without an inline `# shellcheck disable=SCXXXX` naming the specific issue.

Meta · Hooks

Meta · Progress Docs

5

`projects/README.md`, `apps/README.md`, `integrations/README.md` regenerate via `.claude/hooks/bodies/git-precommit.sh` Phase A; MUST NOT manually edit these files — edit the underlying per-project/per-app/per-integration README, and the index regenerates.

Meta · Progress Docs

Journal entries MUST carry session frontmatter listing sessions worked on; MUST NOT commit journal entries with only `date` and `tags` when the session did project work — structured fields feed the telemetry aggregator.

Meta · Progress Docs

When a commit touches files under `projects/<domain>/<date>_<slug>/` (other than that project's README), the commit MUST also update the project's README (`status`, `next`, `blocked-on`, or progress section) OR include `[progress-docs-current]` in the commit message (logged to telemetry); MUST NOT ship project work without synchronized progress docs.

Meta · Progress Docs

Every project README MUST have frontmatter with: `project`, `status` (active|blocked|monitor|completed), `priority` (high|medium|low) for active/blocked/monitor, `created` (YYYY-MM-DD), `tags`, `description`; when `status: blocked`, MUST use `blocked-on:` instead of `next:`; when `status: monitor`, MUST use `monitoring-until: YYYY-MM-DD` naming the observation window end; MUST NOT mark `status: completed` autonomously — user-only.

Meta · Progress Docs

When `/solve` fires, the session block in today's journal MUST contain exactly one line in this format: `**Root-Cause Status:** fixed | deferred-with-project | unknown-pending-verification — <project-path or verification-step>`; `/close` reads this via grep; MUST NOT use backticks on the status label or omit the separator.

Meta · Progress Docs

Meta · Rule Authoring

10

Every new rule, hook, or enforcement MUST cite a real prior failure (journal entry, incident, PR comment) in its evidence date `[YYYY-MM-DD]`; MUST NOT ship rules citing "TBD" or hypothetical scenarios — speculative rules inflate the ruleset without reducing real failures.

Meta · Rule Authoring

A rule MAY leave the always-loaded surface ONLY via a deterministic trigger (hook / `paths:` glob / high-signal keyword where the keyword IS the rule's activation condition).

Meta · Rule Authoring

Files with peer relationships MUST declare them in frontmatter `peers: [<paths>]`; `find-peers.sh` reads frontmatter across changed files; MUST NOT hardcode peer pairs in the find-peers script — adding a peer is a 1-line frontmatter edit.

Meta · Rule Authoring

When promoting a prose rule to a mechanism (hook, linter) and retiring the prose per `@rule:one-behavior-one-surface`, MUST first verify the mechanism's coverage equals the prose's full scope; MUST NOT retire prose broader than the mechanism enforces.

Meta · Rule Authoring

When a rule body contains a literal string that looks like a URL, version, vault name, path, or namespace, MUST classify the string as either a *governance convention* (the string IS what the rule enforces — keep inline) or a *vendor detail* (the string describes an external fact that happens to appear but isn't the rule's enforcement target — reference `integrations/{name}/README.md` or add a forcing function).

Meta · Rule Authoring

A new rule MUST default to a deterministic trigger — mechanized (hook/linter) or path-scoped (`paths:` glob) — so it loads or enforces without Claude choosing.

Meta · Rule Authoring

Every rule MUST end with `[YYYY-MM-DD]` citing a journal entry or commit where the originating failure was observed; MUST NOT use "TBD", "none", or empty brackets — the evidence date is the anti-speculation anchor.

Meta · Rule Authoring

Every `.claude/rules/*.md` file MUST have frontmatter with either `paths: [<globs>]` (path-scoped) or nothing (always-loaded); MUST NOT add arbitrary frontmatter fields — Claude Code only recognizes `paths:`.

Meta · Rule Authoring

A new or edited rule body MUST be minimal-context: the imperative (`MUST`/`MUST NOT`), a `[YYYY-MM-DD]` evidence date, and at most a ≤1-line why.

Meta · Rule Authoring

Rule IDs (section slugs) MUST be stable once shipped — renaming breaks cross-file `@rule:<id>` references; MUST NOT rename a rule without updating all `enforces:` and `@rule:` citations in the same commit (linter enforces).

Meta · Rule Authoring

Meta · Skill Authoring

6

The skill's markdown body MUST match the step graph in frontmatter (every step-ID mentioned in body matches an id in `steps:`, and every frontmatter step has a body section describing it); MUST NOT let body and frontmatter drift — linter checks parity.

Meta · Skill Authoring

Every rule ID in a skill's `enforces: [@rule:<id>]` field MUST resolve to an existing rule section in `.claude/rules/`; MUST NOT reference rules that don't exist — linter rejects dangling enforces.

Meta · Skill Authoring

Every step of `kind: gate` MUST specify the `tool` (`AskUserQuestion`, `shell`, `halt`, `validate-schema`, `agent`, `skill`) and `on_fail` (`halt | warn | continue`); MUST NOT use "prompt the author" as a tool — the tool name must be a concrete mechanism.

Meta · Skill Authoring

Every inter-skill handoff MUST appear in both producer's `handoffs_to:` and consumer's `handoffs_from:`; wire format declared in producer's `writes:` field MUST match consumer's `reads:` field; MUST NOT leave handoffs one-sided — linter flags asymmetry.

Meta · Skill Authoring

Every skill MUST declare in frontmatter: `name`, `description`, `argument-hint` (when the skill takes arguments), `disable-model-invocation` (explicit true/false), `enforces:` (`@rule:<id>` list — may be empty `[]` if the skill enforces no specific rules); MUST declare `peers:` when the skill loads co-located files; MUST declare `steps:` and `handoffs_to:` / `handoffs_from:` when the skill has a gated step graph.

Meta · Skill Authoring

When a skill has gates (AskUserQuestion, halt, shell checks, or inter-skill handoffs), its `steps:` frontmatter MUST declare each step's `id`, `kind` (action | gate), and either an `action:` description (for kind=action) or a `gate:` spec (for kind=gate); MUST NOT use ambiguous intermediate forms.

Meta · Skill Authoring

Meta · Telemetry

5

`.claude/hooks/telemetry/local_aggregator.ts` MUST be invoked weekly to read the last 7 days of journal frontmatter and produce the rule-firing histogram, silent-path-scope failures, root-cause-status distribution, stale-rule list, and correction-vs-rule-deltas signal.

Meta · Telemetry

If the weekly aggregator report is not read for 30 days, the aggregator MUST be marked deprecated; MUST NOT run telemetry that no human consumes — unread metrics are dead weight.

Meta · Telemetry

Session telemetry emits via two surfaces: (a) mechanism-firing log via `.claude/hooks/checks/log-mechanism-fired.sh` sourced from each check script — the primary signal, (b) journal frontmatter `sessions:` block with `rules_should_have_fired`, `implement_audit_rounds`, `root_cause_status` — the supplementary self-noticed signal.

Meta · Telemetry

When the aggregator shows a failure class not dropping over 2 weeks, that data point MUST drive the next rule change (new rule, revised mechanism, or promotion to enforcement); MUST NOT evolve rules on intuition without telemetry support.

Meta · Telemetry

The ai-workflow-telemetry report MUST emit either formatted text or JSON each run; MUST NOT persist a separate report artifact.

Meta · Telemetry

Node Modules Location

1

For ANY TS app in this repo that uses npm dependencies, `node_modules/` MUST live outside `apps/{name}/` and `integrations/{name}/`.

Node Modules Location

Node Stack

3

When about to write a `/tmp/*.ts` or scratch script that calls a typed client from `integrations/{name}/`, MUST first prefer one of three shapes in order: (1) copy the documented one-liner from `integrations/{name}/README.md § "Common invocations"` if it covers the use case; (2) inline via `node --input-type=module -e '...'` for one-shot inspection (no file written, no cleanup, no permission prompt); (3) write the snippet directly into `integrations/{name}/README.md § "Common invocations"` as a new cookbook entry, then run it.

Node Stack

When running TypeScript in this repo — trigger.dev task containers, Hatchet workers (coexistence window only), Daedalus bootstrap scripts (`integrations/*/[name]_auth.ts`), one-off smoke probes, REPL-style invocations on Daedalus — MUST use Node 22 (`node script.ts`, `node --strip-types script.ts`, or `npx tsx script.ts` for transitional callers); MUST NOT use `deno run` or `bun run` for any repo TS code.

Node Stack

TS execution on Node 22 SHOULD prefer the bare `node script.ts` invocation when running on 22.6+ (type-stripping is default-on at that version); MAY pass `--strip-types` explicitly for documentation in `ExecStart` lines and shell snippets, or use `--experimental-strip-types` on 22.x below 22.6.

Node Stack

People

3

All people and entities MUST live in `knowledge/people/{name}/` with a `README.md`; MUST NOT create ad-hoc person files elsewhere (projects/, journal/ references are OK — canonical record lives in people/).

People
people-proactive-enrichment

When a person is mentioned or involved in project work, MUST enrich their `knowledge/people/<name>/` files (README, bio.md, mind.md, professional.md as relevant) in the same session; MUST NOT defer enrichment to "later." [2026-03-12]

People

When editing a `knowledge/people/<name>/README.md`, MUST follow the format of existing people READMEs (bio, professional, mind, health, assessments as applicable); MUST NOT deviate from the pattern without a stated reason in the file.

People

Portal Apps

2

When making a user-facing portal change, MUST update `src/changelog.json` in the same commit; MUST NOT ship user-visible changes without a changelog entry.

Portal Apps

MUST NOT run `npm run build`, `npm install`, or `wrangler pages deploy` locally on portal sites; portal deploys happen automatically on git push (Cloudflare Pages watches the repo).

Portal Apps

Shell

1

Every shell script MUST start with `set -euo pipefail` (after the shebang); MUST NOT rely on default shell safety — errors must surface, not silently propagate.

Shell

Style Anti Ai Tells

1

When drafting any content for a human reader other than Ashkaan (LinkedIn posts, blog articles, email, Slack, client/team notes, texts), MUST pass the AI-voice checks: (a) NO banned AI-tell tokens (SSOT `anti-ai-banned-tokens.txt`, mechanism `check-anti-ai-tokens.sh` blocks the producer apps at commit) and no thesaurus-substitute dodges; (b) NO em dashes — use commas, parentheses, or split the sentence; (c) NO mechanical bold (`**`) — CAPS one word or restructure; (d) take a side, vary sentence rhythm, no rule-of-three, no copula-substitution, no tidy-bow summary sentences, keep at least one rough edge; (e) LinkedIn posts MUST use specific numbers / named frameworks / concrete outcomes and include ≥1 personal-voice marker; (f) when revising a draft Ashkaan wrote himself, MUST preserve his original phrasing and only optimize for the checks above.

Style Anti Ai Tells

Style App Development

17

Every SSR app MUST ship BOTH `src/pages/404.astro` (with `export const prerender = true;` so CF Pages serves the static `404.html` for direct hits) AND `src/pages/[...slug].astro` (SSR catch-all returning **200**, not 404, so Astro's ClientRouter doesn't full-page-reload during View Transitions); MUST NOT ship only one — the prerendered file misses client-side nav, the catch-all alone breaks SEO/bots.

Style App Development

When doing date arithmetic on calendar dates, MUST use `addMonths(dateStr, n)` and `todayLocal()` from `@theme/lib/dates`; MUST NOT use `Date` constructor + `setMonth` patterns — TZ-shift bugs are silent.

Style App Development

When parsing a calendar date for display, MUST use `parseCalendarDate(dateStr)` from `@theme/lib/dates`; MUST NOT use `new Date("YYYY-MM-DD")` — JS spec parses date-only strings as UTC midnight, shifting the day back for US-timezone users.

Style App Development

A calendar date (no time component — complaint date, close date, review date, due date) MUST be stored as `"YYYY-MM-DD"` string (the format `<input type="date">` produces); MUST NOT round-trip through `new Date()` for storage.

Style App Development

Theme-affecting interactive components (`ThemeSwitcher`, `Changelog`, `FeatureRequest`) MUST hydrate with `client:load`; MUST NOT use `client:idle` or `client:visible` — they need to be live before user interaction.

Style App Development

When creating a new internal app, MUST scaffold via `/portal-init [new-name] --from [source-portal-slug]` (clones the closest-matching live portal — `finance.wesolve.tech`, `operations.wesolve.tech`, `sales.wesolve.tech`, `dashboard.ashkaan.me` — into `~/code/[new-name]`, strips `node_modules` + `.git`, resets `name` in `package.json` + `wrangler.toml`, runs `git init`); MUST NOT hand-roll Astro/wrangler/KV setup or recreate it from documentation.

Style App Development

Every app API route MUST guard with `getEmail(request)` from `@theme/lib/auth` (checks `CF-Access-Authenticated-User-Email` header first, falls back to `CF_Authorization` JWT cookie) and return `401` when null; MUST NOT roll custom auth — Cloudflare Access (Zero Trust) is the canonical auth surface.

Style App Development

Every internal app MUST use `DashboardLayout.astro` with: outer `flex h-screen w-full`; sidebar `hidden md:flex w-64 flex-col border-r bg-sidebar` carrying `transition:animate="none"`; content `flex flex-col flex-1 min-w-0`; main `flex-1 overflow-hidden`.

Style App Development

Every app MUST expose `POST /api/feature-request` accepting `{ description, appName, page?, requesterName?

Style App Development

When local-dev needs `locals.runtime.env` (KV, D1, or other bindings), MUST use `wrangler pages dev -- astro dev`; plain `astro dev` runs faster but bindings are `undefined`.

Style App Development

MUST NOT run `npm` commands from the context repo root — run from the app folder or `~`; otherwise `package.json`/lock files/`node_modules` land in the context repo.

Style App Development

Every `.astro` page in an internal app MUST define `const pageInfo: PageInfoEntry` in frontmatter and pass it to `DashboardLayout` via `pageInfo={pageInfo}`; `PageInfo.tsx` reads it for the per-page context modal (purpose, mechanics, data sources, notes); presence is CLAUDE-applied (no automated check); MUST NOT ship a page without `pageInfo`.

Style App Development

When persisting user preferences in an app, MUST use `getItem/setItem/syncFromServer` from `@theme/lib/prefs` (synchronous localStorage read, fire-and-forget background PUT to `/api/prefs` → KV); the app MUST expose `/api/prefs` (GET/PUT) endpoints — the edge-app scaffold includes them; MUST NOT bypass the localStorage-first pattern.

Style App Development

When styling an app, MUST use the base radius `var(--radius)` = 10px (variants `rounded-sm` 6px through `rounded-2xl` 18px), navy-tinted shadows in light mode and black-tinted in dark (`shadow-card`/`shadow-card-hover`), blue glows via `shadow-glow`/`shadow-glow-lg` for interactive elements; MUST NOT hand-roll one-off shadow stacks.

Style App Development

When styling or building UI in an internal app, MUST use the shared theme's canonical surface — design tokens, the typography trio, utility classes, and animation classes — from `theme/` (git subtree of `github.com/Ashkaan/internal-apps-shared.wesolve.tech`, the SSOT); MUST NOT introduce app-specific palette/font overrides, re-implement a themed pattern inline, or write custom keyframes for the standard cases.

Style App Development

A timestamp (precise moment in time — `createdAt`, `updatedAt`, `lastUpdated`) MUST be stored as `new Date().toISOString()` (`"2026-04-09T19:42:11.000Z"`); display via `formatTimestamp(isoStr, options?)` from `@theme/lib/dates`.

Style App Development

Every app `wrangler.toml` MUST declare: `name = "app-name"`, `compatibility_date = "2025-09-01"`, `compatibility_flags = ["nodejs_compat_v2"]`, `pages_build_output_dir = "dist"`, `max_duration = 30`; MUST NOT change compatibility date/flags without verifying every binding still works.

Style App Development

Style Biome

1

The repo's TypeScript formatter + linter is Biome `^2.4.15` <!-- rule-ref: allowed --> installed at `~/.local/lib/quality/node_modules/@biomejs/biome` (per `@rule:node-modules-out-of-repo`).

Style Biome

Style Code Conventions

12

TypeScript and shell identifiers MUST follow these casing conventions: - **Variables, functions, methods (TS):** camelCase (`dayStart`, `fetchJson()`).

Style Code Conventions

Shell scripts MUST follow these formatting + safety conventions (naming covered by @rule:naming-conventions): - 2-space indentation (no tabs), 80-char hard line limit.

Style Code Conventions

Exported functions MUST have an explicit return-type annotation; MUST NOT rely on inference for exports — exports are the contract.

Style Code Conventions

Thrown error messages MUST be sentence case, no trailing period, active voice, with context (`throw new Error(\`Cannot refresh token for ${path}: ${r.status}\`)`); MUST NOT use vague messages ("Token refresh error.").

Style Code Conventions

TypeScript imports MUST be grouped in this order, blank-line separated: (1) type imports, (2) third-party packages, (3) `integrations/` paths, (4) relative imports; MUST NOT mix groups.

Style Code Conventions

When defining an object shape, MUST use `interface` (extendable, better error messages); when defining unions, intersections, or aliases, MUST use `type`; result types MUST be named (not inline) to document the contract; for untyped API responses, MUST use `as ApiResponse` cast on `await res.json()`.

Style Code Conventions

When catching errors, MUST log or push to warnings; MUST NOT use empty `catch {}` — silent failures are bugs.

Style Code Conventions

TypeScript code MUST fetch secrets via the canonical resolver: `getCred(CREDS.x, "field")` from `integrations/1password/op_helper.ts` (typed UUID-keyed credential map per `projects/homelab/2026-05-01_credential-architecture/`).

Style Code Conventions

MUST NOT write JSDoc on internal/non-exported code — types are the documentation; MAY write a one-line `/** description */` + `@param` tags ONLY on shared utility exports (functions imported from `integrations/{name}/`).

Style Code Conventions

TypeScript code MUST be explicit; MUST NOT use `Proxy`, `eval`, or runtime metaprogramming patterns — implicit behavior is hard to trace and harder to debug.

Style Code Conventions

Node.js scripts on Daedalus MUST use `.js` extensions in relative imports (Node ESM requirement); MUST NOT omit the extension.

Style Code Conventions

Before committing TypeScript changes, MUST pass the code-quality checks: `biome-quality` (lint), `gitleaks`, `check-refs`; MUST NOT commit with quality-gate failures — fix at the source.

Style Code Conventions

Style Design

2

Browser automation MUST go through the `npx playwright` CLI directly OR an inline Node script that imports the `playwright` npm package.

Style Design

When designing for slideshows or presentations, MUST keep body / bullets / supporting copy at `text-lg` (18px) or larger; eyebrows + mono captions MAY sit at `text-base` (16px) with bolder weight + tighter tracking to keep hierarchy.

Style Design

Style Longform Format

2

When drafting LinkedIn posts, MUST use plain text with line breaks, CAPS (sparingly), and `→` arrows; MUST NOT use markdown `**bold**`, `# headers`, or `- bullets`; LinkedIn renders them as raw characters.

Style Longform Format

When drafting blog articles or long-form posts, MUST use sequential heading levels (H1 → H2 → H3); MUST NOT skip from H1 to H3; AI does this routinely.

Style Longform Format

Trigger Dev Stack

5

Every trigger.dev app under `apps/{name}/` (flat) or `apps/<domain>/{name}/` (domain member) MUST use the B2 layout: docs + config at the app root (`README.md`, `SPEC.md`, `package.json`, `tsconfig.json`, `trigger.config.ts`), task declarations under `src/tasks/*.task.ts`, pure helpers under `src/lib/*.ts`, tests under `tests/*.test.ts`.

Trigger Dev Stack

Every `apps/{name}/trigger.config.ts` MUST set `runtime: "node-22"`; MUST NOT use `"node"` (which selects 21.7.3), `"bun"`, or any other value — single-runtime Node 22 across the repo (per `@rule:node-runtime-is-node-22`) is the coherence guarantee.

Trigger Dev Stack

A trigger.dev **project** is a deploy bundle (one built image → one GHCR slot → one run-history view).

Trigger Dev Stack

All trigger.dev apps share ONE off-repo dep cache at `~/.local/lib/trigger-dev/` (bootstrapped one-time via `bash .claude/hooks/checks/trigger-dev-build.sh bootstrap`).

Trigger Dev Stack

Every `apps/{name}/package.json` that depends on trigger.dev MUST pin `@trigger.dev/sdk` to `^4.4.6` <!-- rule-ref: allowed --> (the latest stable at the 2026-05-14 plan-write, verified via `npm view @trigger.dev/sdk version`); MUST NOT depend on `@trigger.dev/sdk@beta`, `@v4-beta`, `@v4-prerelease`, or any 0.0.0-dated dist-tag — the trigger.dev team publishes dozens of dated dist-tags (`next`, `prerelease`, `chat-prerelease`, etc.); accepting any of those into the repo's lockfile pulls non-reproducible code into production.

Trigger Dev Stack

Ui Behavior

9

Interface copy MUST be specific ("Enter email", not "Enter value"), active ("Save changes", not "Changes will be saved"), and MUST tell the user what to do rather than only what happened.

Ui Behavior

Destructive actions MUST prefer undo over a confirmation dialog — users click through confirmations without reading, so a dialog buys less safety than a reversal window.

Ui Behavior

Before shipping any view, MUST enumerate expected behavior — not merely "test with" — at each axis.

Ui Behavior

Text that will be translated MUST use logical properties (`margin-inline-start`, `padding-inline`, `border-inline-end`) rather than left/right, and MUST mirror directional glyphs under RTL (`[dir="rtl"] .arrow { transform: scaleX(-1) }`).

Ui Behavior

Every interactive element MUST define all eight states before it ships: default, hover, focus, active, disabled, loading, error, success.

Ui Behavior

Loading states MUST NOT use jokey placeholder copy ("Herding pixels", "Teaching robots to dance", "Consulting the magic 8-ball") — instantly recognizable as machine-generated and the fastest way to make a product feel cheap.

Ui Behavior

Dropdowns, tooltips, popovers, and menus MUST escape their clipping ancestor.

Ui Behavior

UI work MUST hold Core Web Vitals: LCP <2.5s, INP <200ms, CLS <0.1, and 60fps (~16ms/frame) on interaction.

Ui Behavior

Responsive behavior MUST key off input capability where the concern is input — `@media (pointer: coarse)` for touch-sized padding, `@media (hover: hover)` to gate hover-only affordances — and off width only where the concern is space.

Ui Behavior

Ui Craft

9

When defining or extending a palette, MUST author in OKLCH (lightness 0–100%, chroma ~0–0.4, hue 0–360) rather than HSL — HSL lightness is not perceptually uniform, so equal-lightness steps look uneven.

Ui Craft

Text and UI MUST meet WCAG AA: body 4.5:1, large text (18px+, or 14px bold) 3:1, UI components and focus indicators 3:1.

Ui Craft

When a directive seems to call for a new primitive — a color, gradient, radius, shadow, font, or decorative effect not already in the repo's `DESIGN.md` — MUST NOT invent it.

Ui Craft

When loading a webfont, MUST declare fallback metric overrides (`size-adjust`, `ascent-override`, `descent-override`, `line-gap-override`) so the fallback occupies the same box — unmatched fallback metrics are the actual cause of font-swap layout shift.

Ui Craft

Custom transitions MUST take their duration from the band matching the change — 100–150ms instant feedback (press, toggle, color), 200–300ms state change (menu, tooltip, hover), 300–500ms layout change (accordion, modal, drawer), 500–800ms entrance (page load, hero reveal) — and MUST use one of three easings: `--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1)`, `--ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1)`, `--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1)`.

Ui Craft

MUST NOT ship the generated-UI signature set: gradient text (`background-clip: text` on a gradient), glassmorphism panels, neon accents on near-black, cyan/purple gradient pairs, cream/sand body backgrounds (`oklch(97% 0.01 60)`-family), thick one-side accent borders on rounded cards, repeating tiny uppercase kickers above every section heading, sequential `01/02/03` section markers, icon-tile stacks, thin-border-plus-wide-shadow cards, and scroll-fade-rise on every section.

Ui Craft

Before acting on any subjective design directive ("bolder", "quieter", "cleaner", "more premium"), MUST resolve which register the surface is in and state it.

Ui Craft

Spacing MUST come from a 4pt scale (4, 8, 12, 16, 24, 32, 48, 64, 96) — 8pt-only is too coarse, since 12px is needed between 8 and 16.

Ui Craft

When defining a type ramp, MUST pick one ratio and commit to it: brand surfaces ≥1.25 (1.25 major third / 1.333 perfect fourth / 1.5 perfect fifth), product surfaces 1.125–1.2.

Ui Craft