Skip to content

Commit fd746fb

Browse files
committed
chore(repo): move changeset tooling to root
1 parent c716e05 commit fd746fb

4 files changed

Lines changed: 104 additions & 53 deletions

File tree

AGENTS.md

Lines changed: 89 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,43 @@
22

33
ЦЕЛЬ: Создавать математически доказуемые решения через функциональную парадигму с полным разделением чистых вычислений и контролируемых эффектов.
44

5-
Всегда начинай свой ответ с постановки задачи Deep Research "I am looking for code that does <requested functionality>, is there existing code that can do this?" Любое решение строится на математических инвариантах, доказуемых свойствах и проверяемых источниках. Код создается только после формального понимания проблемы и построения архитектурной модели.
5+
МОДЕЛЬ РАССУЖДЕНИЯ:
6+
7+
- Не выдавать “личные мнения”. Формировать вывод как результат симуляции профессионального обсуждения релевантных ролей
8+
(архитектор Effect/FP, ревьюер типов, страж CORE↔SHELL, тест-инженер).
9+
- Если запрос сформулирован как “что думаешь”, отвечать в терминах аргументов ролей и выбирать решение
10+
по критериям инвариантов, типовой безопасности и тестируемости (если пользователь явно просит выбор — выбрать и обосновать).
11+
12+
ПРАВИЛО ПРОЦЕССА (НЕ ФОРМАТ ОТВЕТА):
13+
В начале работы (внутренне) формулировать Deep Research вопрос:
14+
"I am looking for code that does <requested functionality>, is there existing code that can do this?"
15+
Далее:
16+
17+
- если доступен проект/код — сперва искать и переиспользовать существующие паттерны (минимальный корректный diff),
18+
- если проект недоступен — опираться на предоставленный контекст и явно фиксировать допущения,
19+
- код писать только после формального понимания задачи (типы/инварианты → архитектура → код → тесты),
20+
- источники указывать только если реально использован внешний материал; иначе `SOURCE: n/a`.
21+
22+
Любое решение строится на математических инвариантах, доказуемых свойствах и проверяемых источниках. Код создается только после формального понимания проблемы и построения архитектурной модели.
623

724
АРХИТЕКТУРНЫЕ ПРИНЦИПЫ:
825
═══════════════════════════════
926

1027
🏗️ **FUNCTIONAL CORE, IMPERATIVE SHELL**:
1128

1229
- CORE: Исключительно чистые функции, неизменяемые данные, математические операции
13-
- SHELL: Все эффекты (IO, сеть, БД) изолированы в тонкой оболочке
30+
- SHELL: Все эффекты (IO, сеть, БД, env/process) изолированы в тонкой оболочке
1431
- Строгое разделение: CORE никогда не вызывает SHELL
1532
- Зависимости: SHELL → CORE (но не наоборот)
1633

1734
🔒 **ТИПОВАЯ БЕЗОПАСНОСТЬ**:
1835

19-
- Никогда: `any`, `unknown`, `eslint-disable`, `ts-ignore`, `as` (кроме обоснованных случаев)
20-
- Всегда: исчерпывающий анализ union types через `.exhaustive()`
36+
- Никогда: `any`, `eslint-disable`, `ts-ignore`
37+
- `unknown`: допускается ТОЛЬКО на boundary (SHELL) как вход в декодирование (например, `@effect/schema`);
38+
после декодинга `unknown` не должен выходить наружу boundary-модуля
39+
- `as`: запрещён в обычном коде; допускается ТОЛЬКО в одном “аксиоматическом” модуле (бренды/конструкторы/константы),
40+
дальше использование без кастов
41+
- Всегда: исчерпывающий анализ union types через `.exhaustive()` / `Match.exhaustive`
2142
- Внешние зависимости: только через типизированные интерфейсы
2243
- Ошибки: типизированы в сигнатурах функций, не runtime exceptions
2344

@@ -27,19 +48,24 @@
2748
- Композиция через `pipe()` и `Effect.flatMap()`
2849
- Dependency injection через Layer pattern
2950
- Обработка ошибок без try/catch
51+
- Запрещено в продукт-коде: `async/await`, raw Promise chains (`then/catch`), `Promise.all`
52+
- Interop с Promise/исключениями — только в SHELL через `Effect.try` / `Effect.tryPromise` (с типизированным маппингом ошибок)
53+
- Ресурсы с финализацией — только через `Effect.acquireRelease` + `Effect.scoped`
3054

3155
ОБЯЗАТЕЛЬНЫЕ ТРЕБОВАНИЯ:
3256
═══════════════════════════
3357

3458
1. **ЧИСТОТА ФУНКЦИЙ**:
3559

3660
```typescript
37-
// ✅ ПРАВИЛЬНО - чистая функция
38-
const calculateTotal = (items: readonly Item[]): Money =>
39-
items.reduce((sum, item) => sum + item.price, 0 as Money)
61+
// ✅ ПРАВИЛЬНО - чистая функция (без эффектов, без мутаций)
62+
type Money = number
63+
64+
const calculateTotal = (items: ReadonlyArray<Item>): Money =>
65+
items.reduce((sum, item) => sum + item.price, 0)
4066

4167
// ❌ НЕПРАВИЛЬНО - нарушение чистоты
42-
const calculateTotal = (items: Item[]): Money => {
68+
const calculateTotalImpure = (items: Item[]): Money => {
4369
console.log("Calculating total") // ПОБОЧНЫЙ ЭФФЕКТ!
4470
return items.reduce((sum, item) => sum + item.price, 0)
4571
}
@@ -50,9 +76,9 @@ const calculateTotal = (items: Item[]): Money => {
5076
```typescript
5177
// CHANGE: <краткое описание изменения>
5278
// WHY: <математическое/архитектурное обоснование>
53-
// QUOTE(ТЗ): "<дословная цитата требования>"
79+
// QUOTE(ТЗ): "<дословная цитата требования>" | n/a
5480
// REF: <REQ-ID из RTM или номер сообщения>
55-
// SOURCE: <ссылка с дословной цитатой из внешнего источника>
81+
// SOURCE: <ссылка с дословной цитатой из внешнего источника> | n/a
5682
// FORMAT THEOREM: <∀x ∈ Domain: P(x) → Q(f(x))>
5783
// PURITY: CORE | SHELL - явная маркировка слоя
5884
// EFFECT: Effect<Success, Error, Requirements> - для shell функций
@@ -84,7 +110,7 @@ const calculateTotal = (items: Item[]): Money => {
84110

85111
```typescript
86112
// Switch statements are forbidden in functional programming paradigm.
87-
// How to fix: Use Effect.Match instead.
113+
// How to fix: Use Match with exhaustive coverage.
88114
// Example:
89115
import { Match } from "effect"
90116

@@ -154,17 +180,17 @@ const PostgresMessageRepository = Layer.effect(
154180
7. **CONVENTIONAL COMMITS С ОБЛАСТЯМИ**:
155181

156182
```bash
157-
feat(core): add message validation with mathematical constraints
183+
feat(core): add message validation with mathematical constraints
158184

159-
- Implements pure validation functions for message content
160-
- Adds invariant: ∀ msg: valid(msg) → sendable(msg)
161-
- BREAKING CHANGE: Message.content now requires non-empty string
185+
- Implements pure validation functions for message content
186+
- Adds invariant: ∀ msg: valid(msg) → sendable(msg)
187+
- BREAKING CHANGE: Message.content now requires non-empty string
162188

163-
fix(shell): resolve database connection pooling issue
189+
fix(shell): resolve database connection pooling issue
164190

165-
perf(core): optimize message sorting algorithm to O(n log n)
191+
perf(core): optimize message sorting algorithm to O(n log n)
166192

167-
docs(architecture): add formal specification for FCIS pattern
193+
docs(architecture): add formal specification for FCIS pattern
168194
```
169195

170196
8. **ОБЯЗАТЕЛЬНЫЕ БИБЛИОТЕКИ**:
@@ -181,22 +207,39 @@ const PostgresMessageRepository = Layer.effect(
181207
9. **СТРОГАЯ ТИПИЗАЦИЯ ВНЕШНИХ ЗАВИСИМОСТЕЙ**:
182208

183209
```typescript
184-
// Все внешние сервисы через Effect + Layer
185-
class DatabaseService extends Context.Tag("DatabaseService")
186-
DatabaseService,
187-
{
188-
readonly query: <T>(sql: string, params: readonly unknown[]) => Effect.Effect<T, DatabaseError>
189-
readonly transaction: <T>(op: Effect.Effect<T, DatabaseError>) => Effect.Effect<T, DatabaseError>
190-
}
191-
>() {}
192-
193-
class HttpService extends Context.Tag("HttpService")
194-
HttpService,
195-
{
196-
readonly get: <T>(url: string) => Effect.Effect<T, HttpError>
197-
readonly post: <T>(url: string, body: unknown) => Effect.Effect<T, HttpError>
198-
}
199-
>() {}
210+
// Все внешние сервисы через Effect + Layer.
211+
// Boundary-данные должны быть типизированы; "unknown" допускается только как вход в Schema decoding внутри boundary-модуля.
212+
213+
type SqlValue = string | number | boolean | null | bigint | Uint8Array | Date
214+
215+
class DatabaseService extends Context.Tag("DatabaseService")
216+
DatabaseService,
217+
{
218+
readonly query: <T>(
219+
sql: string,
220+
params: ReadonlyArray<SqlValue>
221+
) => Effect.Effect<T, DatabaseError>
222+
readonly transaction: <T>(
223+
op: Effect.Effect<T, DatabaseError>
224+
) => Effect.Effect<T, DatabaseError>
225+
}
226+
>() {}
227+
228+
type Json =
229+
| null
230+
| boolean
231+
| number
232+
| string
233+
| ReadonlyArray<Json>
234+
| { readonly [k: string]: Json }
235+
236+
class HttpService extends Context.Tag("HttpService")
237+
HttpService,
238+
{
239+
readonly get: <T>(url: string) => Effect.Effect<T, HttpError>
240+
readonly post: <T>(url: string, body: Json) => Effect.Effect<T, HttpError>
241+
}
242+
>() {}
200243
```
201244

202245
10. **ТЕСТИРОВАНИЕ С МАТЕМАТИЧЕСКИМИ СВОЙСТВАМИ**:
@@ -215,17 +258,20 @@ describe("Message invariants", () => {
215258
)
216259
)
217260

218-
// Unit тесты с мок-зависимостями (быстрые)
219-
it("should handle send message use case", async () => {
220-
const result = await pipe(
261+
// Unit тесты с мок-зависимостями (быстрые) — без async/await
262+
it.effect("should handle send message use case", () =>
263+
pipe(
221264
sendMessageUseCase(validCommand),
222265
Effect.provide(MockMessageRepository),
223266
Effect.provide(MockNotificationService),
224-
Effect.runPromise
267+
Effect.tap((messageId) =>
268+
Effect.sync(() => {
269+
expect(messageId).toEqual(expectedMessageId)
270+
})
271+
),
272+
Effect.asVoid
225273
)
226-
227-
expect(result).toEqual(expectedMessageId)
228-
})
274+
)
229275
})
230276
```
231277

@@ -246,6 +292,8 @@ describe("Message invariants", () => {
246292
- Нет прямых обращений к внешним системам в CORE
247293
- Все Effect'ы композируются через pipe()
248294
- TSDoc содержит инварианты и сложность
295+
- Нет `async/await`, raw Promise chains, `try/catch` для логики, `console.*` в продукт-коде
296+
- Любые boundary-данные декодируются (например, `@effect/schema`) прежде чем попасть в домен
249297

250298
**BEFORE MERGE**:
251299

package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,20 @@
1010
"scripts": {
1111
"build": "pnpm --filter @effect-template/app build",
1212
"check": "pnpm --filter @effect-template/app check",
13+
"changeset": "changeset",
14+
"changeset-publish": "node -e \"if (!process.env.NPM_TOKEN) { console.log('Skipping publish: NPM_TOKEN is not set'); process.exit(0); }\" && changeset publish",
15+
"changeset-version": "changeset version",
1316
"dev": "pnpm --filter @effect-template/app dev",
1417
"lint": "pnpm --filter @effect-template/app lint",
1518
"lint:tests": "pnpm --filter @effect-template/app lint:tests",
1619
"test": "pnpm --filter @effect-template/app test",
1720
"typecheck": "pnpm --filter @effect-template/app typecheck",
1821
"start": "pnpm --filter @effect-template/app start"
1922
},
23+
"devDependencies": {
24+
"@changesets/changelog-github": "^0.5.2",
25+
"@changesets/cli": "^2.29.8"
26+
},
2027
"repository": {
2128
"type": "git",
2229
"url": "git+https://github.com/ProverCoderAI/effect-template.git"

packages/app/package.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
"scripts": {
1010
"build": "vite build --ssr src/app/main.ts",
1111
"dev": "vite build --watch --ssr src/app/main.ts",
12-
"changeset": "changeset",
13-
"changeset-publish": "node -e \"if (!process.env.NPM_TOKEN) { console.log('Skipping publish: NPM_TOKEN is not set'); process.exit(0); }\" && changeset publish",
14-
"changeset-version": "changeset version",
1512
"lint": "npx @ton-ai-core/vibecode-linter src/",
1613
"lint:tests": "npx @ton-ai-core/vibecode-linter tests/",
1714
"check": "pnpm run typecheck",
@@ -56,8 +53,6 @@
5653
},
5754
"devDependencies": {
5855
"@biomejs/biome": "^2.3.8",
59-
"@changesets/changelog-github": "^0.5.2",
60-
"@changesets/cli": "^2.29.8",
6156
"@effect/eslint-plugin": "^0.3.2",
6257
"@effect/language-service": "latest",
6358
"@effect/vitest": "^0.27.0",

pnpm-lock.yaml

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)