chore(.agents/skills,AGENTS.md): new harness prompt

This commit is contained in:
Neko Ayaka
2026-08-04 19:24:12 +08:00
parent 55bd16e175
commit 7de2e2bf46
7 changed files with 151 additions and 204 deletions
@@ -0,0 +1,43 @@
---
name: enforce-rules-for-unocss
description: Enforce AIRI's UnoCSS, Vue styling, shared UI component, animation, icon, and color-mode practices. Use when creating, editing, refactoring, or reviewing Vue templates, component styles, utility classes, UnoCSS configuration, animations, icons, UI primitives, packages/ui components, or VueUse dark-mode behavior in the AIRI monorepo.
---
# Enforce AIRI UnoCSS Rules
Apply these rules to every affected UI file in AIRI.
## Compose Utility Classes Readably
- Prefer UnoCSS over Tailwind CSS.
- In Vue templates, bind grouped class arrays for readability:
```vue
:class="[
'px-2 py-1',
'flex items-center',
'bg-white/50 dark:bg-black/50',
]"
```
- Do not use long inline class strings such as `class="px-2 py-1 flex items-center bg-white/50 dark:bg-black/50"`.
- Do not use attributify-style groups such as `px="2" py="1" flex="~ items-center" bg="white/50 dark:black/50"`.
- When touching legacy utility classes, progressively refactor them into readable grouped arrays.
## Reuse Project Styling Infrastructure
- Use or extend shortcuts and rules in `uno.config.ts` when styles should be standardized or reused.
- Search `apps/stage-web/src/styles` for existing animations before adding one. Reuse or extend an existing animation when it fits.
- Consult `apps/stage-web/tsconfig.json` and `uno.config.ts` when configuration context is needed.
- Keep animations intuitive, lively, and readable.
## Build on Shared UI Primitives
- Build primitives on `@proj-airi/ui`, which is based on reka-ui, instead of raw DOM controls.
- Read `docs/ai/context/ui-components.md` for the component API and `packages/ui/src/components/Form` for implementation patterns.
- When adding or updating a component in `packages/ui`, update `docs/ai/context/ui-components.md` with its description, props, slots, and emits.
- Use Iconify icon sets instead of bespoke SVGs.
## Preserve Theme Behavior
- When using VueUse `useDark`, set `disableTransition: false` or use an existing composable from `packages/ui`.
@@ -0,0 +1,4 @@
interface:
display_name: "Enforce UnoCSS Rules"
short_description: "Apply AIRI styling and component standards"
default_prompt: "Use $enforce-rules-for-unocss to implement or review AIRI UI styling."
@@ -0,0 +1,51 @@
---
name: enforce-rules-for-vitest
description: Enforce AIRI's testing and Vitest practices. Use when creating, editing, reviewing, or debugging tests; reproducing a reported bug or issue; changing Vitest configuration; mocking IPC, services, providers, platform APIs, or imports; or diagnosing test import and runtime-boundary failures in the AIRI monorepo.
---
# Enforce AIRI Vitest Rules
Apply these rules to every test change in AIRI.
## Choose the Test Scope
- Use the Vitest project that owns the affected code and keep runs targeted for speed.
- Grow component and end-to-end coverage progressively. Prefer Vitest browser mode when the behavior depends on DOM or Web Platform APIs.
- Use the smallest automated test that faithfully exercises the behavior: prefer a unit test, then the smallest suitable higher-level test.
## Reproduce Bugs Before Fixing Them
1. For an investigated bug or issue, try to add a test-only reproduction before changing production code.
2. When reproduction is possible, include the tracker identifier in the test case name:
- Use `Issue #<number>` for a GitHub issue.
- Use the Linear issue key for an internal Linear bug.
3. Put the actual report URL in a comment directly above the regression test. Use the GitHub issue URL, Discord message or thread URL, or Linear issue URL as appropriate.
4. Confirm that the reproduction fails for the reported reason before implementing the fix.
## Mock Real Boundaries
- Mock Electron IPC and Electron services with `vi.fn` or `vi.mock`; never require a real Electron runtime.
- For external providers and services, add mock-based tests and, when feasible, integration-style tests guarded by environment variables, but do not mock Pinia, Vue components. Vitest import mocks are allowed for these boundaries.
- Assert observable behavior, including mock calls and parameters, with explicit `expect` statements.
- Prefer one assertion per line so failures remain readable.
## Preserve Runtime Integrity
- Do not test impossible runtime states. Avoid assertions against constants that cannot change or object mutations that can only occur inside the same test setup.
- Do not replace `globalThis` properties or built-in modules with direct `Object.defineProperty(...)` mocks.
- When behavior depends on a different Node global or built-in state, use `node:worker_threads` to load an isolated worker or build a minimal CLI reproduction.
- For DOM and Web Platform APIs, use Vitest browser mode instead of hard-mocking platform internals. Progressively refactor existing direct platform mocks when touched.
## Fix Import Boundaries, Not Tests
Never use Vitest mocks, hoisting, dynamic imports, `as unknown as`, or test-only alternate import paths to conceal a real import failure.
If a test cannot import a module, investigate and fix the production boundary:
- package exports and declarations;
- import-time side effects;
- mixed Node and browser type dependencies;
- circular imports;
- an incorrect public module shape.
Keep the test importing the same supported boundary that production consumers use.
@@ -0,0 +1,4 @@
interface:
display_name: "Enforce Vitest Rules"
short_description: "Apply AIRI testing and Vitest standards"
default_prompt: "Use $enforce-rules-for-vitest to implement or review tests under AIRI's testing standards."
@@ -1,89 +0,0 @@
---
name: server-gateway-refactor
description: Use when refactoring AIRI apps/server routes that mix Hono route wiring, business operation orchestration, external gateway calls, billing, rate limiting, telemetry, or websocket session state. Applies especially to OpenAI-compatible, speech, Stripe, and websocket gateway surfaces.
---
# Server Gateway Refactor
Use this skill when an `apps/server` route file has grown into a mixed transport/business/infra module and the user wants it engineered rather than merely split by line count.
## First Read
Start from the exact route file the user named. Read nearby tests and domain services before editing. Use `rg` for call sites and avoid deleting legacy routes without checking tests/docs/env references.
Look for these responsibilities:
- HTTP/WebSocket transport shape: Hono routes, auth, request parsing, response mounting, upgrade setup.
- Gateway operation shape: authenticated user, parsed body/query, operation id, model/provider routing, external calls.
- Infra behavior: billing, rate limiting, telemetry/tracing, request logs, PostHog, retries, cache.
- Domain services: persistence, Stripe records, character/provider ownership, chat messages, flux transactions.
## Boundary Rules
- Keep Hono middleware as Hono middleware only when it can run from raw `Context`: auth, config availability, static files, IP-level limits.
- Use gateway middleware when the decision needs parsed gateway context: user id, operation id, request body, requested model, resolved model, streaming lifecycle, usage, or billing state.
- Prefer route-group scoped middleware for endpoint-specific behavior:
- Good: `gateway.route('openai').use('chat.completions', rateLimit).post(...)`
- Avoid: global gateway `.use('chat.completions', ...)` when the middleware is only meaningful for one endpoint group.
- Do not create handler files that only pass through parse + operation. Inline thin adapters in the route index unless they hide meaningful protocol decisions.
- Do not split by execution order alone. A module boundary should own a policy, operation, middleware, or external boundary.
## Naming
- File names use kebab-case, not camelCase.
- Use HTTP names for transport files and operation names for business files.
- Prefer `middlewares/` for both Hono and gateway middleware in this project when they are part of a route gateway surface.
- Prefer `operations/<operation>/index.ts` for reusable operation orchestration.
- Avoid `gateway` as a domain name unless the module really owns route/runtime composition.
## Preferred Shape
For gateway-like HTTP surfaces:
```ts
const gateway = createXGateway(deps)
.useHono('*', '*', authGuard)
.useHono('surface', '/path/*', configGuard(...))
const surfaceRoutes = gateway.route('surface')
.use('operation.id', operationMiddleware(...))
.post('/path', surface.handler(
'operation.id',
async (c) => parseInput(c),
operation(deps),
))
.route
```
Keep route index readable:
- It should show route groups, endpoint paths, and endpoint-scoped middleware.
- It may inline small parse adapters.
- It should not contain long external-provider workflows, webhook switches, or billing settlement logic.
## Candidate Signals
Use this pattern when:
- One route file exceeds roughly 200-300 lines and mixes route wiring with external provider orchestration.
- There are endpoint-specific middleware needs that cannot be represented as Hono middleware.
- Tests describe operation behavior more than route matching.
- The route has multiple business operations under one transport surface.
Do not force this pattern when:
- A CRUD route is already thin and delegates to a domain service.
- The route mostly mounts framework-owned handlers, static assets, or metadata endpoints.
- The logic belongs in an existing domain service instead of a new route gateway.
## Verification
After changes, run targeted validation before broader checks:
```sh
pnpm exec vitest run apps/server/src/routes/<route>/route.test.ts
pnpm -F @proj-airi/server typecheck
pnpm exec eslint <changed files>
```
If full `pnpm lint` fails from unrelated repo-wide issues, report that separately and keep targeted lint evidence.
+44 -114
View File
@@ -86,51 +86,23 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
- Favor clear module boundaries; shared logic goes in `packages/`.
- Keep runtime entrypoints lean; move heavy logic into services/modules.
- Prefer functional patterns + DI (`injeca`) for testability.
- Use Valibot for schema validation; keep schemas close to their consumers.
- Use Eventa (`@moeru/eventa`) for structured IPC/RPC contracts where needed.
- Use `errorMessageFrom(error)` from `@moeru/std` to extract error messages instead of manual patterns like `error instanceof Error ? error.message : String(error)`. Pair with `?? 'fallback'` when a default is needed.
- Do not add backward-compatibility guards. If extended support is required, write refactor docs and spin up another Codex or Claude Code instance via shell command to complete the implementation with clear instructions and the expected post-refactor shape.
- If the refactor scope is small, do a progressive refactor step by step.
- When modifying code, always check for opportunities to do small, minimal progressive refactors alongside the change.
- For new feature requirements or requirement-related tasks involving `node:*` built-in modules, DOM operations, Vue composables, React hooks, Vite plugins, or GitHub Actions workflows, always do deep research for suitable existing libraries or open source modules first. Before choosing any library, always ask the user to choose and help judge which option is right. Never choose generalized utility libraries on your own (for example, `es-toolkit`, utilities from `github.com/unjs`, or tiny tools from `github.com/tinylib`) without explicit user confirmation. If the user is working spec-driven, list candidate choices in a clear and concise Markdown comparison table.
## Styling & Components
## Enforced Repository Skills
- Prefer Vue v-bind class arrays for readability when working with UnoCSS & tailwindcss: do `:class="['px-2 py-1','flex items-center','bg-white/50 dark:bg-black/50']"`, don't do `class="px-2 py-1 flex items-center bg-white/50 dark:bg-black/50"`, don't do `px="2" py="1" flex="~ items-center" bg="white/50 dark:black/50"`; avoid long inline `class=""`. Refactor legacy when you touch it.
- Use/extend UnoCSS shortcuts/rules in `uno.config.ts`; add new shortcuts/rules/plugins there when standardizing styles. Prefer UnoCSS over Tailwind.
- Check `apps/stage-web/src/styles` for existing animations; reuse or extend before adding new ones. If you need config references, see `apps/stage-web/tsconfig.json` and `uno.config.ts`.
- Build primitives on `@proj-airi/ui` (reka-ui) instead of raw DOM; see [`docs/ai/context/ui-components.md`](docs/ai/context/ui-components.md) for the full component API reference and `packages/ui/src/components/Form` for implementation patterns.
- **When adding or updating components in `packages/ui`**, update [`docs/ai/context/ui-components.md`](docs/ai/context/ui-components.md) to reflect the change (props, slots, emits, description).
- Use Iconify icon sets; avoid bespoke SVGs.
- Animations: keep intuitive, lively, and readable.
- `useDark` (VueUse): set `disableTransition: false` or use existing composables in `packages/ui`.
## Testing Practices
- Vitest per project; keep runs targeted for speed.
- For any investigated bug or issue, try to reproduce it first with a test-only reproduction before changing production code. Prefer a unit test; if that is not possible, use the smallest higher-level automated test that can still reproduce the problem.
- When an issue reproduction test is possible, include the tracker identifier in the test case name:
- GitHub issues: include `Issue #<number>`
- Internal bugs tracked in Linear: include the Linear issue key
- Add the actual report link as a comment directly above the regression test:
- GitHub issue URL for GitHub reports
- Discord message or thread URL for IM reports
- Linear issue URL for internal bugs
- Mock IPC/services with `vi.fn`/`vi.mock`; do not rely on real Electron runtime.
- For external providers/services, add both mock-based tests and integration-style tests (with env guards) when feasible. You can mock imports with Vitest.
- Grow component/e2e coverage progressively (Vitest browser env where possible). Use `expect` and assert mock calls/params.
- When writing tests, prefer line-by-line `expect` or assertion statements.
- Avoid writing tests for impossible runtime states, such as `expect` against constants that never change, or asserting object mutations that can only happen inside the same Vitest case setup.
- Avoid mocking `globalThis` or built-in modules by directly using `Object.defineProperty(...)`. If needed, use `node:worker_threads` to load another worker and simulate that situation, or build a mini CLI to reproduce and verify behavior. For DOM and Web Platform APIs, prefer Vitest browser mode instead of hard-mocking platform internals. If tests already use those patterns, progressively refactor them.
- Do not use Vitest mocks, hoisting, dynamic imports, `as unknown as`, or test-only alternate import paths to maliciously bypass real import problems. If a test cannot import a module, investigate the actual compile/runtime boundary: package exports, side effects, mixed Node/browser type dependencies, circular imports, and whether the public module shape is wrong. Fix the boundary instead of hiding the failure in the test.
- For testing, Vitest, regression reproduction, mocks, or test import-boundary work, always use [`enforce-rules-for-vitest`](.agents/skills/enforce-rules-for-vitest/SKILL.md).
- For UnoCSS, Vue styling, UI components, animations, icons, or color-mode work, always use [`enforce-rules-for-unocss`](.agents/skills/enforce-rules-for-unocss/SKILL.md).
## TypeScript / IPC / Tools
- Keep JSON Schemas provider-compliant (explicit `type: object`, required fields; avoid unbounded records).
- Favor functional patterns + DI (`injeca`); avoid new class hierarchies unless extending browser APIs (classes are harder to mock/test).
- For Electron, and backend related packages, use `injeca` for dependency management; avoid new class hierarchies unless extending browser APIs (classes are harder to mock/test).
- Centralize Eventa contracts; use `@moeru/eventa` for all events.
- Import types from the module or package that owns the contract. Do not redeclare external/public contracts locally just to use a narrower subset, and do not route type imports through local runtime assembly modules when the original side-effect-free type source is available.
- Do not use inline type imports such as `typeof import('...').x` or `import('...').Type` to avoid normal module boundaries. Export explicit shared types from the owning module, import external contract types from their owning package, or split a dedicated side-effect-free type module when runtime imports would pull in the wrong environment.
- Do not directly modify or override `tsconfig.json` to make an import/type error disappear. First investigate compilation behavior, `package.json` `exports` declarations, type declarations, and whether the dependency exposes the intended browser/node entrypoints.
- When Node-only and browser-only types are mixed through one import chain, split the type declarations into a neutral type file and keep runtime modules environment-specific. Avoid importing values from modules that carry side effects just to obtain types.
- If a wrong export or missing export causes an error, trace the full import chain and side-effect chain before changing imports at the leaf. Prefer fixing package/module exports and the owning boundary over adding local workaround imports.
@@ -174,47 +146,29 @@ the pull request.
the `note`. Keep each sentence short and active: translators read them, and most do not read English
as a first language.
## CSS/UNO
- Use/extend UnoCSS shortcuts in `uno.config.ts`.
- Prefer grouped class arrays for readability; refactor legacy inline strings when possible.
## Readability, Naming, and Comments
### Naming
- Use kebab-case for all file names.
- Prefer names that rely on the module boundary for context instead of repeating package, product, protocol, or transport prefixes inside every symbol. A well-named module should let exported functions use short action-first names; repeat the larger context only when the symbol crosses a boundary where that context is no longer obvious.
- Name functions after the domain operation they perform, not after the implementation layer that happens to contain them. This keeps call sites readable after refactors and avoids names becoming stale when code moves between files.
- Avoid names that encode multiple layers of ownership into one symbol. If a name needs several qualifiers to be understandable, reconsider the module boundary or introduce a clearer local concept.
- Use nouns for resolved domain concepts and verbs for transformations or side effects. When a function derives a policy/configuration from an event or request, name the domain result explicitly so callers understand what decision is being made.
- Prefer classes for runtime/browser APIs and substantial business modules when the class owns state, lifecycle, or a stable domain boundary. Prefer FP for pure transformations and local helpers.
- Use dependency injection only at real external boundaries: database, model runtime, queue, Redis/cache, filesystem, network, clock, environment, and feature gates. Do not introduce `Dependencies`/`Deps` objects for internal functions that only call sibling helpers or forward parameters.
- Comments should reduce reader uncertainty, not increase documentation volume.
- Write comments where a reader would otherwise ask why this case can happen, why this branch is ignored, why this fallback exists, why this order matters, what state changed here, what external side effect just happened, or what protocol/invariant this line is preserving.
- Good comments explain hidden intent, constraints, ownership, invariants, ordering, side effects, protocol shape, or non-obvious fallback behavior.
- Bad comments translate code into English, restate names/types, or exist only to satisfy hover documentation.
- Important implementation comments should live near the confusing line or branch, not only on exported declarations.
- For calculation-heavy code, prefer inline comments near the intermediate values and branches that need explanation. Do not rely only on function-level JSDoc when the hard part is a coordinate system, unit conversion, clamp, rounding rule, aggregation, fallback, or precedence decision.
- Apply this especially to geometry, graphics and shader math, billing or metering, analytics or statistics, UI layout and positioning, ranking or scoring, and normalization code.
- Format longer comments as short paragraphs separated by blank comment lines. Do not compress background, symptom, rejected alternatives, final rationale, and references into one dense block.
- For investigation-heavy comments, prefer this order when useful: source/context, observed failure, why the obvious fix is insufficient, chosen fix, and references/removal condition.
- Do not add broad comments like `// Config`, `// Host`, or `// Update state` unless they explain a non-obvious boundary or transition.
- Add clear, concise comments for utils, math, OS-interaction, algorithm, shared, and architectural functions that explain non-obvious intent, invariants, constraints, or why the code is needed.
- When using a workaround, add a `// NOTICE:` comment explaining why, the root cause, and any source context. If validated via `node_modules` inspection or external sources (e.g., GitHub), include relevant line references and links in code-formatted text.
- When moving/refactoring/fixing/updating code, keep still-accurate comments with the code. Remove obsolete comments rather than preserving their history in source; explain notable removals in review notes when needed.
- Avoid stubby/hacky scaffolding; prefer small refactors that leave code cleaner.
- Let module boundaries provide context. Avoid repeating package, product, protocol, or transport names in symbols unless the symbol crosses a boundary where that context would otherwise be lost.
- Name functions after domain operations rather than implementation layers.
- Use nouns for resolved domain concepts and verbs for transformations or side effects.
- If a symbol needs several ownership qualifiers to be understandable, reconsider the module boundary or introduce a clearer domain concept.
### Comments
- Comments should explain information the code cannot express clearly: intent, constraints, ownership, invariants, precedence, lifecycle, ordering, side effects, protocol shape, or non-obvious fallbacks.
- Do not add comments that only restate names, types, or visible operations.
- Place implementation comments next to the branch, calculation, transition, or side effect they explain.
- For calculation-heavy code, explain non-obvious coordinate systems, units, conversions, clamps, rounding, aggregation, and precedence beside the relevant intermediate values or branches.
- Prefer clearer names, types, and structured state over comments that compensate for hidden or encoded concepts.
- Keep accurate comments when moving code and remove comments that no longer describe current behavior.
- Format investigation-heavy comments as short paragraphs. When useful, cover context, observed failure, why the obvious fix is insufficient, the chosen fix, and its removal condition or references.
- Use markers:
- `// TODO:` follow-ups
- `// REVIEW:` concerns/needs another eye
- `// NOTICE:` magic numbers, hacks, important context, external references/links
### JSDoc
- Use JSDoc for public APIs, shared boundaries, exported types, and non-trivial exported functions/classes.
- Do not use JSDoc as a substitute for explaining complex implementation branches.
- Do not force `Use when / Expects / Returns` blocks onto internal helpers, object literal methods, simple pass-through methods, or interface/type top-level comments when they only restate names and signatures.
- For interfaces and type aliases, keep top-level JSDoc short. Put field-specific semantics on fields when the field has non-obvious units, defaults, ownership, lifecycle, freshness, or compatibility behavior.
- For exported functions/classes that form a real API boundary, JSDoc should explain what the boundary guarantees, when to use it, and what assumptions callers must respect.
- For internal implementation details, prefer precise names and branch-local comments over large JSDoc blocks.
- `// TODO:` for follow-up work.
- `// REVIEW:` for concerns that need another opinion.
- `// NOTICE:` for workarounds, magic values, external constraints, and other important non-obvious context.
### Fallbacks and Precedence
@@ -235,35 +189,28 @@ as a first language.
- Document what happens to pending requests on timeout, close, unload, dispose, and publish failure.
- When cleanup spans multiple owners, keep the ordering visible and explain why the order matters.
- When returning a snapshot, fallback value, stale value, or cached value, document freshness semantics at the return site.
### Helper Extraction
- Before extracting a private helper, ask what decision it hides.
- Keep logic inline when the helper only names a single execution step and is used once.
- Extract a helper when it owns reusable policy, parsing, normalization, lifecycle, cleanup, error handling, protocol validation, or a cross-call invariant.
- Do not hide special cases inside generic helpers if doing so moves the explanation away from the branch where readers need it.
- If a helper manipulates encoded keys, cache ownership, session ownership, filesystem paths, route names, or protocol-shaped data, document the encoding/invariant near the helper or replace the encoding with a clearer structure.
- For watchers, event listeners, and async background work, make ownership and shutdown behavior explicit: what starts and stops the work, whether duplicate starts are allowed, and what happens to in-flight work during unload or dispose.
### Readability Refactors
- If a comment is needed to explain hidden state, encoded data, protocol envelopes, or lifecycle transitions, first consider whether named types, structured state, or a small policy function would make the concept explicit.
- Readability-only changes should preserve runtime behavior. If behavior changes, add focused tests and document the contract change explicitly.
- For watchers, event listeners, and async background work, make ownership and shutdown behavior explicit: what starts it, what stops it, whether duplicate starts are allowed, and what happens to in-flight work during unload or dispose.
## Module Design
- Prefer deep modules over shallow modules. A module should hide a meaningful decision: policy, persistence boundary, protocol/schema contract, scheduling semantics, model prompt contract, domain invariant, or lifecycle concern.
- Do not split code by execution order alone. A module boundary should represent a stable responsibility that can be understood without reading all sibling files.
- Keep cohesive domain flows together until there is proven pressure to split. A 200-400 line cohesive module is preferable to several shallow modules that pass the same context/options through each other.
- Prefer classes for runtime or browser APIs and substantial business modules that own state, lifecycle, or a stable domain boundary. Prefer functions for pure transformations and local helpers.
- Use dependency injection only at external boundaries such as databases, model runtimes, queues, caches, filesystems, networks, clocks, environments, and feature gates. Do not introduce dependency objects for internal functions that only call sibling helpers or forward parameters.
- Before creating a new `createXService` or `XDependencies`, verify that `X` adds policy, validation, state, retry/error handling, IO boundary, or a reusable abstraction. If not, keep it as a private helper or inline it.
- Avoid pass-through services such as `createXService({ yService })` when `X` adds no meaningful policy, validation, state, or abstraction.
- Do not extract tiny one-call helper functions just to name an implementation step, reduce line count, or make tests easier to write. Keep short logic inline when the helper does not hide a real decision, policy, IO boundary, normalization rule, retry/error handling, lifecycle concern, or reusable domain concept.
- Extract a helper only when it is reused by multiple production call sites, hides non-trivial branching/IO/parsing/normalization/error policy, names a stable domain concept, or forms part of a public/package API.
- Keep special cases close to the branch they affect. If a helper manipulates encoded keys, ownership, filesystem paths, routes, or protocol-shaped data, make that invariant explicit in its structure, name, or nearby documentation.
- Test through stable public behavior. Do not create new exports, dependency bags, or wrapper services only to make private implementation details mockable.
- Keep reusable domain contracts and rendering/building logic in the package that owns that domain. Runtime entrypoints should wire dependencies and call those boundaries instead of inlining large reusable contracts.
## PR / Workflow Tips
- When asked to create, open, publish, or prepare a pull request, always use the repo-local `create-pr` skill. It invokes `create-screenshots-of-ui` for user-visible changes and requires before/after screenshots to be uploaded as GitHub user assets in the PR body.
- Rebase pulls; branch naming `username/feat/short-name`; clear commit messages (gitmoji is prohibited).
- Summarize changes, how tested (commands), and follow-ups.
- Improve legacy you touch; avoid one-off patterns.
@@ -271,7 +218,6 @@ as a first language.
- Maintain structured `README.md` documentation for each `packages/` and `apps/` entry, covering what it does, how to use it, when to use it, and when not to use it.
- Always run `pnpm type-check` and `pnpm lint` after finishing a task.
- Use Conventional Commits for commit messages (e.g., `feat(<package name>): add runner reconnect backoff`).
- For new feature requirements or requirement-related tasks involving `node:*` built-in modules, DOM operations, Vue composables, React hooks, Vite plugins, or GitHub Actions workflows, always do deep research for suitable existing libraries or open source modules first. Before choosing any library, always ask the user to choose and help judge which option is right. Never choose generalized utility libraries on your own (for example, `es-toolkit`, utilities from `github.com/unjs`, or tiny tools from `github.com/tinylib`) without explicit user confirmation. If the user is working spec-driven, list candidate choices in a clear and concise Markdown comparison table.
- Before planning or writing new utilities/functions, always search for existing internal implementations first. If the logic could become shared utilities, proactively propose that shared approach to users and developers.
## TypeScript Coding Regulations
@@ -280,8 +226,7 @@ These guidelines apply to all TypeScript code across the monorepo:
- Do not create commits during implementation for this spec.
- For implemented modules, use Vitest whenever possible to verify behavior and passing tests.
- During test implementation, every workaround must include a clear and easy-to-understand `// NOTICE:` comment for reference.
- Use the following workaround comment format whenever a workaround is introduced:
- Every workaround must use this `// NOTICE:` format:
```ts
// NOTICE:
// Why this workaround is needed.
@@ -290,20 +235,12 @@ These guidelines apply to all TypeScript code across the monorepo:
// Removal condition (when it can be safely deleted).
```
- Prefer type generics wherever possible. Do not use `any`. Only use `as unknown as <target expected type>` when avoiding it is nearly impossible and the type cannot be fixed safely.
- For public APIs, package-level exports, shared architectural boundaries, and non-trivial exported functions/classes/types, include clear `/** ... */` JSDoc that explains the contract, assumptions, side effects, lifecycle, or return guarantees callers actually need.
- Use JSDoc for public APIs, package-level exports, shared architectural boundaries, and non-trivial exported functions, classes, and types. Document only contract details that the signature cannot express, such as assumptions, side effects, lifecycle, and return guarantees.
- Avoid exporting helper functions only to satisfy tests or documentation rules. Keep implementation helpers private unless production code reuses them.
- Avoid JSDoc on trivial one-line helpers, local projections, and pass-through functions; use precise names instead.
- Do not use fixed JSDoc section templates for ordinary exported functions/classes/types. Write natural API documentation that explains the contract, non-obvious constraints, side effects, lifecycle expectations, and return guarantees that callers actually need.
- Keep JSDoc short when the name and type already explain the behavior. Add detail only for information that is not visible from the signature.
- For functions that include workarounds, include a `NOTICE:` explanation.
- For exported test helpers or non-obvious reusable test fixtures, include examples when they clarify intended usage. Do not add `@example` comments to ordinary `describe`, `it`, or `expect*` calls.
- For all exported interfaces, especially configurable options, document:
- What each interface/type represents.
- Put detailed field semantics on the fields themselves instead of repeating them in one large interface-level comment block.
- If the interface or type uses generic parameters, document them with `@param`.
- `@default` for every option that has a default value.
- For interface and type JSDoc, keep the top-level comment focused on what the type represents. Do not use function-style `Use when`, `Expects`, or `Returns` sections on interfaces or type aliases. Put detailed meaning, defaults, and behavioral notes on the individual fields or methods instead of restating every field in the interface-level block.
- For generic type parameters in JSDoc, use `@param` entries to explain what each type parameter represents.
- Do not add JSDoc to trivial helpers, local projections, or pass-through functions. Avoid fixed section templates that restate names and signatures; prefer precise names and branch-local comments for implementation details.
- For exported test helpers or non-obvious reusable test fixtures, include `@example` when it clarifies intended usage.
- Do not attach JSDoc or `@example` blocks to ordinary `describe`, `it`, or `expect*` calls.
- For exported interfaces and type aliases, keep the top-level JSDoc focused on what the type represents and put detailed semantics on the relevant fields. Document generic parameters with `@param`, and add `@default` to every option that has a default value.
- For runner and CLI entrypoints, `/** ... */` JSDoc is required and must include a clear ASCII call-stack diagram using `{@link ...}` references where applicable. For server orchestrators, add the call-stack diagram only when it clarifies a stable architecture boundary; do not add diagrams to shallow glue code.
- Use this call-stack section format in orchestrator/runner/CLI JSDoc:
```ts
@@ -318,20 +255,18 @@ These guidelines apply to all TypeScript code across the monorepo:
* -> {@link VievalScheduledTask}[]
*/
```
- Wherever math, OS, exec, process, args, networking, files, or directories are involved, add comments explaining the purpose and why the code is needed when the intent is not obvious from names and local context. For calculation-heavy code, prefer inline comments beside the calculation process over declaration-only JSDoc.
- For non-obvious OS, exec, process, argument, networking, file, or directory handling, explain the constraint or purpose near the relevant code.
- Prefer `es-toolkit` first when creating utilities.
- For error handling, prefer `@moeru/std` patterns whenever possible.
- For exported normalizers, shared normalizers, or non-obvious local normalizers that normalize outputs, formats, filenames, or values (excluding config default normalization), add `/** ... */` with before/after examples.
- For exported normalizers, shared normalizers, or non-obvious local normalizers that normalize outputs, formats, filenames, or values (excluding config default normalization), add `/** ... */` JSDoc with an `@example` showing representative input and output.
- Use this normalizer documentation format:
```ts
/**
* Normalizes <target>.
*
* Before:
* - "ExampleInput"
*
* After:
* - "example-output"
* @example
* normalizeTarget('ExampleInput')
* // => 'example-output'
*/
```
- Do not move everything into constants. One-time or two-time constants should remain near usage (typically near the top after imports) with clear `/** ... */` explaining why.
@@ -359,12 +294,7 @@ These guidelines apply to all TypeScript code across the monorepo:
When reviewing complex TypeScript modules, check:
- Can I identify the module's owned state and external side effects within one screen?
- Are persisted state, runtime state, cache state, session state, and external side effects separated by names or comments?
- Are protocol envelopes, route names, ids, and correlation keys named or documented?
- Are special branches and fallback cases explained next to the branch?
- Are stale/fresh/cache/snapshot semantics visible at the return site?
- Are cleanup and dispose semantics explicit?
- Are helper functions hiding real policy, or just hiding the lines where explanation is needed?
- Do comments explain why decisions exist, or mostly repeat what the code says?
- Would a reader understand why this code is shaped this way without opening three neighboring files?
- Are owned state, external side effects, lifecycle transitions, cleanup, and freshness semantics identifiable without tracing several neighboring files?
- Are protocol envelopes, correlation keys, isolation rules, and fallback precedence explicit at their decision points?
- Do module and helper boundaries hide meaningful policy rather than merely forwarding context or obscuring special cases?
- Do comments explain non-obvious decisions beside the relevant code without restating names, types, or visible operations?
+5 -1
View File
@@ -48,7 +48,11 @@ export default defineConfig({
// (where the consequent is the error itself, not its `.message`). Antfu's
// default no-restricted-syntax patterns are preserved alongside.
'no-restricted-syntax': [
'warn',
'error',
{
selector: 'TSImportType',
message: 'Do not use inline type imports. Import types explicitly from their owning module, or split a dedicated side-effect-free type module when a runtime import would pull in the wrong environment.',
},
{
selector: 'ConditionalExpression[test.type=\'BinaryExpression\'][test.operator=\'instanceof\'][test.right.name=\'Error\'][consequent.type=\'MemberExpression\'][consequent.property.name=\'message\']',
message: 'Avoid `error instanceof Error ? error.message : ...`. Use `errorMessageFrom(error)` from \'@moeru/std\' (or `errorMessageFromUnknown(error, fallback)` from \'@proj-airi/stage-shared\'). Pair with `?? \'fallback\'` when a default is needed.',