跳到正文
cccskills
免费在 GitHub 公开

ayunis-core-backend

Ayunis Core backend specifics — module boundaries, key files, and project conventions. Complements the nestjs-hexagonal-backend skill.

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core星标 34(GitHub 34 + cccskills 0)

实测评分 未实测

查看安装方法

包含的文件(1)

  • SKILL.md9.5 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Ayunis Core Backend

Load the nestjs-hexagonal-backend skill first — this skill adds project-specific context on top.

Working Directory

All commands run from ayunis-core-backend/:

cd ayunis-core-backend

Module Boundaries

The backend enforces strict bounded contexts:

  • src/domain/* — Core business logic (agents, threads, messages, runs, models, tools, prompts, sources, RAG, etc.)
  • src/iam/* — Identity and access management (auth, users, orgs, subscriptions, quotas, teams, etc.)
  • src/common/* — Shared infrastructure only (base classes, utilities)
  • src/admin/* — Super admin routes

Cross-module communication uses exported use cases, not ports/adapters. When module A needs functionality from module B, module A imports B's module and injects B's use case directly — do NOT create a port in A with an adapter that wraps B. Ports (abstract interfaces) are only for infrastructure boundaries within a module (e.g., a repository port implemented by a persistence adapter).

The injection point must be module A's application layer (a use case or service) — never an infrastructure adapter. Infrastructure must not import use cases (enforced by the adapters-no-use-cases dependency-cruiser rule; inbound/driving adapters like queue consumers and scheduled tasks are exempt). If an adapter seems to need another module's functionality because the relevant data only appears mid-fetch, restructure so the adapter returns raw data and the application layer makes the cross-module call (e.g. the Cheerio URL adapter returns raw bytes, and RetrieveUrlUseCase decides whether to delegate PDFs to the file-retrieval use case).

Persistence records are an exception to the cross-module rule. TypeORM schema records (*.record.ts) may reference records from other modules via @ManyToOne / @OneToOne + @JoinColumn to declare foreign-key relationships — this is required for referential integrity (see the typeorm-migrations skill). The "don't cross modules" guidance applies to application-layer code (use cases, services), not to schema records, which are infrastructure that must mirror the DB.

Before modifying any module, read its SUMMARY.md. See ARCHITECTURE.md for the complete module index.

Place Responsibilities Before Writing Code

Before adding a backend file, inventory the target module's existing directories and inspect the two closest analogues. Follow the module's established vocabulary and placement; do not create a file directly under a layer root such as application/ when its role belongs in an existing subdirectory.

Assign each responsibility to one layer before implementation:

  • Presenter DTOs validate transport input completely, including conditional requirements and exact confirmation values.
  • Controllers translate validated HTTP input, invoke use cases, and translate results. They do not repeat DTO validation, make business decisions, or query repositories.
  • Application models hold data and behavior shared across commands or use cases without becoming domain state. Put them under application/models/.
  • Application services/use cases coordinate policy decisions and cross-module calls. Prefer a service when multiple entry points enforce the same policy.
  • Domain objects own business invariants intrinsic to persisted domain state.
  • Infrastructure records and mappers mirror persistence; they do not decide application policy.

Before finalizing the design, check for the same policy or entity lookup in adjacent controllers and use cases. Consolidate duplicated decisions at the narrowest shared application boundary and avoid loading the same entity twice in one request path.

ConfigService access — match the registerAs namespace

Backend config is loaded via registerAs('<namespace>', () => ({...})) factories. The lookup key must be prefixed with the namespace, or ConfigService.get(...) returns undefined and the call site silently uses a missing value (e.g. apiKey: undefined → 401 at runtime, not at boot).

For model providers, the namespace is models:

// CORRECT ✓
this.configService.get<string>('models.mistral.apiKey')
this.configService.get<string>('models.openai.apiKey')
this.configService.get<string>('models.anthropic.apiKey')

// WRONG ✗ — un-namespaced; returns undefined
this.configService.get<string>('mistral.apiKey')

Before adding a new provider handler, grep an existing one for the exact key path rather than transcribing from the .env variable name — the env-var → config-key mapping lives in the registerAs factory and the namespace is easy to drop on the floor.

User Context

User identity comes from ContextService, not method parameters:

// CORRECT ✓
const userId = this.contextService.get('userId');

// WRONG ✗
async execute(command: { userId: string })  // Don't pass context

Outbound Provider Errors

Any code path that calls an external provider over the network (LLM inference, embeddings, Mistral OCR, vendor SDKs) must classify failures through the shared taxonomy in src/common/errors (AYC-538) — do not invent per-adapter "service busy / timeout" error classes:

const providerError = wrapProviderFailure(error, { provider, modelId });
if (providerError) throw providerError; // ProviderUnavailableError family
// else: ApplicationError rethrows as-is; upstream 4xx and unknown errors
// stay in the module's own error family (potentially OUR bug — must remain
// a distinct, first-occurrence-alerting AppSignal incident)
  • wrapProviderFailure returns ProviderConnectionError (502) / ProviderTimeoutError (504) / ProviderServerError (502) for transport failures (errno, undici, TLS codes, SDK wrapper names — cause chains are walked) and upstream 5xx; undefined for everything else.
  • Grouping contract: each error sets name === code === PROVIDER_UNAVAILABLE_<CLASS>_<PROVIDER> so AppSignal opens one incident per provider+failure-class. There is only one reporting path — setError() is span.recordException(), and AppSignal's SpanProcessor forwards every exception span event from every span — and OpenTelemetry derives the grouping key exception.type as code ?? name (truthy code wins). Setting both to the same string is what makes the key stable no matter which span records it. Never add these errors to ignoreErrors in appsignal.cjs — per-occurrence notifications are disabled AppSignal-side so rate-based anomaly triggers keep working.
  • Suppressions are a registry, not a config literal: every ignoreErrors / ignoreRequestHook / disabled-instrumentation entry lives in SUPPRESSIONS in appsignal-hooks.cjs (AYC-563) and must carry a reason and an owning ticket. The spec asserts each ignoreErrors entry against a real instance of its error, because an entry naming a class whose instances carry a .code suppresses nothing at all — silently. appsignal.cjs only consumes the registry; it decides nothing.
  • Reference choke points: stream-inference.use-case.ts, get-inference.use-case.ts (models), embed-text.use-case.ts (embeddings), mistral-file-retriever.handler.ts (OCR, incl. ProviderRequestRejectedError for machine-generated 4xx — but not 401/403 auth-config bugs).
  • Not a provider: fetching customer-pasted URLs. Keep UrlRetrieverRetrievalError (422) and enrich its metadata with classifyTransportError() output instead.
  • This hand-written translate-catch at outbound choke points is the sanctioned exception to use-case-reference's "no hand-written try/catch error boundaries" rule.

Key Files

PurposeLocation
Architecture & module indexARCHITECTURE.md
Module summariessrc/[area]/[module]/SUMMARY.md
TypeORM configsrc/db/datasource.ts
OpenAPI spechttp://localhost:PORT/api/docs (when running)

[!IMPORTANT] Always update ARCHITECTURE.md and the module's SUMMARY.md file if necessary!

Running Tests

Run backend tests with pnpm test from ayunis-core-backend/ — never bare jest or npx jest.

cd ayunis-core-backend
pnpm test                      # full suite
pnpm test <path-or-pattern>    # a subset

pnpm test runs Jest through the project's configured runner (Node ESM flags, module-name mapping). Invoking jest / npx jest directly bypasses that setup, and the ESM-only p-queue dependency then fails to resolve — the embeddings-throttle and ingest-bulk-content RAG suites blow up with module-resolution errors.

Those failures are an artifact of the wrong invocation, not a real regression. Do not report them as "pre-existing failures on main" or hang caveats on a PR summary because of them — re-run with pnpm test and they pass. A suite that fails under pnpm test is a genuine signal worth investigating.

Runtime Health Check

When runtime validation is required by the repository's Proportional Workflow, verify the service responds before exercising the changed behavior:

curl http://localhost:PORT/api/health

The port depends on the dev slot — check with ./dev status from the repo root.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Query Ayunis deployed-environment telemetry in AppSignal — incidents, occurrence counts and error traces via the `appsignal-cli` binary; structured log attributes (user.id, org.id, nestjs.context, custom metadata) via the v2 logs API. Use when investigating a production/staging incident or log data — NOT for local dev logs (see backend-debugging).

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core342026年10月26日 更新

Frontend development in ayunis-core. Use when creating, modifying, or debugging frontend code (React, Feature-Sliced Design, API client).

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core342026年10月26日 更新

Debug backend runtime errors (500s, crashes, unexpected behavior). Use when something is broken at runtime — not for writing new code.

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core342026年10月26日 更新

Shared test fixtures for backend specs — port mock factories, domain builders, and shared IDs in a per-module testing/ folder. Use when a module's *.use-case.spec.ts files repeat the same mock-repository literal, inline entity construction, or hardcoded UUIDs across files.

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core342026年10月26日 更新

Analyze Cursor Bugbot PR comments through a root-cause lens: is the finding valid, and is the suggested fix the real fix or a bandaid over a deeper problem? Use when the user asks to check, assess, or triage bugbot comments.

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core342026年10月26日 更新

Self-review local changes before creating a PR. Use when the user says "review", "review my code", "check my changes", or wants confidence before submitting.

中文简介正在准备中。当前显示原文说明。

ayunis-core/ayunis-core342026年10月26日 更新

查看 ayunis-core 的全部技能

报告此技能的问题